提高业力:这种对 transform_attribute 的隐式调用是如何工作的? (或者没有?)
Boost karma: how does this implicit call to transform_attribute work? (or doesn't?)
我有以下一段代码似乎工作正常(我基于 reuse parsed variable with boost karma 的语义操作)。
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/sequence.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/support_attributes.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
using namespace boost::spirit;
struct DataElement
{
DataElement(const std::string& s) : str_(s) {}
const std::string& str() const { return str_; }
std::string& str() { return str_; }
std::string str_;
};
using Data = std::vector<std::shared_ptr<const DataElement>>;
namespace boost {
namespace spirit {
namespace traits {
template<>
struct transform_attribute<std::shared_ptr<const DataElement> const, const DataElement&, karma::domain>
{
using type = const DataElement&;
static type pre(const std::shared_ptr<const DataElement>& val) { return *val; }
};
}
}
}
BOOST_FUSION_ADAPT_ADT(
DataElement,
(std::string&, const std::string&, obj.str(), obj.str())
);
template<typename Iterator>
struct TheGrammar: public karma::grammar<Iterator, Data()>
{
TheGrammar(): karma::grammar<Iterator, Data()>(start)
{
start %= -(elt % karma::eol);
elt %=
karma::lit("'some prefix'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some infix 1'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some infix 2'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some suffix'")
;
}
karma::rule<Iterator, Data()> start;
karma::rule<Iterator, const DataElement&()> elt;
};
int main(void)
{
Data vec = {
std::make_shared<DataElement>("one"),
std::make_shared<DataElement>("two"),
std::make_shared<DataElement>("three"),
std::make_shared<DataElement>("four"),
std::make_shared<DataElement>("five"),
std::make_shared<DataElement>("six"),
std::make_shared<DataElement>("seven"),
std::make_shared<DataElement>("eight"),
};
using iterator_type = std::ostream_iterator<char>;
iterator_type out(std::cout);
TheGrammar<iterator_type> grammar;
return karma::generate(out, grammar, vec);
}
我想了解一些事情:
- 为什么我不需要在任何地方使用
karma::attr_cast
?我的 start
规则是 std::shared_ptr
的向量,而 elt
规则适用于实际对象常量引用。我最初尝试 attr_cast
但一无所获,只是半心半意地尝试了这个版本,以防万一它起作用了,而且它起作用了......
- 如果我完全注释掉我的自定义
transform_attribute
,为什么它仍然可以编译?是否提供了一些默认的 std::shared_ptr<T>
-> T&
transform_attribute?我找不到太多,但也许我没有找对地方?
- 如果我注释掉我的自定义
transform_attribute
,如上所述,代码仍然编译,但在运行时显然有一些内存损坏。 karma::string
产生垃圾。在某种程度上,我可以理解一定会发生一些有趣的事情,因为我什至没有告诉 karma 如何从我的 shared_ptr
到达对象。它编译的是实际的 error/bug 吗?
非常感谢您的宝贵时间和帮助!
- 每个规则都有一个隐含的 attr_cast 声明的属性类型
有些时候 Spirit 的类型兼容性规则变得混乱。我所看到的只是它与字符串类型是一个容器这一事实有关。沿途的某个地方 "copy-constructs" 一个 std::string 似乎有长度 97332352。毫不奇怪,这本身是错误的并且恰好触发了 UB,因为最终传递给 memset
的范围重叠:
Source and destination overlap in memcpy(0x60c1040, 0x5cd2c90, 97332352)
at 0x4C30573: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x401B26: copy (char_traits.h:290)
by 0x401B26: _S_copy (basic_string.h:299)
by 0x401B26: _S_copy_chars (basic_string.h:342)
by 0x401B26: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) [clone .isra.53] (basic_string.tcc:229)
by 0x402442: _M_construct_aux<char*> (basic_string.h:195)
by 0x402442: _M_construct<char*> (basic_string.h:214)
by 0x402442: basic_string (basic_string.h:401)
by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:172)
by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:184)
by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:217)
by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:237)
by 0x402442: pre (attributes.hpp:23)
是的,这是一个 QoI 问题。
问题通常出在 c++ 的隐式转换上。指针类型有许多意想不到的转换。共享指针确实将它们的上下文转换为 bool。
更多注释:
您的融合改编似乎有缺陷:val
未在 setter
中使用
BOOST_FUSION_ADAPT_ADT(DataElement, (std::string &, const std::string &, obj.str(), obj.str() = val))
你正在做很多我学会避免的事情。
- 我更喜欢无语义动作的规则:Boost Spirit: "Semantic actions are evil"?
- 我不在 Spirit 中使用共享指针 grammars/generators How can I use polymorphic attributes with boost::spirit::qi parsers?(可以说,在生成器设置中问题不大!)
- 我不做ADT适配(UB太容易被咬了)
我有以下一段代码似乎工作正常(我基于 reuse parsed variable with boost karma 的语义操作)。
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/sequence.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/support_attributes.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>
using namespace boost::spirit;
struct DataElement
{
DataElement(const std::string& s) : str_(s) {}
const std::string& str() const { return str_; }
std::string& str() { return str_; }
std::string str_;
};
using Data = std::vector<std::shared_ptr<const DataElement>>;
namespace boost {
namespace spirit {
namespace traits {
template<>
struct transform_attribute<std::shared_ptr<const DataElement> const, const DataElement&, karma::domain>
{
using type = const DataElement&;
static type pre(const std::shared_ptr<const DataElement>& val) { return *val; }
};
}
}
}
BOOST_FUSION_ADAPT_ADT(
DataElement,
(std::string&, const std::string&, obj.str(), obj.str())
);
template<typename Iterator>
struct TheGrammar: public karma::grammar<Iterator, Data()>
{
TheGrammar(): karma::grammar<Iterator, Data()>(start)
{
start %= -(elt % karma::eol);
elt %=
karma::lit("'some prefix'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some infix 1'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some infix 2'")
<< karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
<< karma::lit("'some suffix'")
;
}
karma::rule<Iterator, Data()> start;
karma::rule<Iterator, const DataElement&()> elt;
};
int main(void)
{
Data vec = {
std::make_shared<DataElement>("one"),
std::make_shared<DataElement>("two"),
std::make_shared<DataElement>("three"),
std::make_shared<DataElement>("four"),
std::make_shared<DataElement>("five"),
std::make_shared<DataElement>("six"),
std::make_shared<DataElement>("seven"),
std::make_shared<DataElement>("eight"),
};
using iterator_type = std::ostream_iterator<char>;
iterator_type out(std::cout);
TheGrammar<iterator_type> grammar;
return karma::generate(out, grammar, vec);
}
我想了解一些事情:
- 为什么我不需要在任何地方使用
karma::attr_cast
?我的start
规则是std::shared_ptr
的向量,而elt
规则适用于实际对象常量引用。我最初尝试attr_cast
但一无所获,只是半心半意地尝试了这个版本,以防万一它起作用了,而且它起作用了...... - 如果我完全注释掉我的自定义
transform_attribute
,为什么它仍然可以编译?是否提供了一些默认的std::shared_ptr<T>
->T&
transform_attribute?我找不到太多,但也许我没有找对地方? - 如果我注释掉我的自定义
transform_attribute
,如上所述,代码仍然编译,但在运行时显然有一些内存损坏。karma::string
产生垃圾。在某种程度上,我可以理解一定会发生一些有趣的事情,因为我什至没有告诉 karma 如何从我的shared_ptr
到达对象。它编译的是实际的 error/bug 吗?
非常感谢您的宝贵时间和帮助!
- 每个规则都有一个隐含的 attr_cast 声明的属性类型
有些时候 Spirit 的类型兼容性规则变得混乱。我所看到的只是它与字符串类型是一个容器这一事实有关。沿途的某个地方 "copy-constructs" 一个 std::string 似乎有长度 97332352。毫不奇怪,这本身是错误的并且恰好触发了 UB,因为最终传递给
memset
的范围重叠:Source and destination overlap in memcpy(0x60c1040, 0x5cd2c90, 97332352) at 0x4C30573: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) by 0x401B26: copy (char_traits.h:290) by 0x401B26: _S_copy (basic_string.h:299) by 0x401B26: _S_copy_chars (basic_string.h:342) by 0x401B26: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) [clone .isra.53] (basic_string.tcc:229) by 0x402442: _M_construct_aux<char*> (basic_string.h:195) by 0x402442: _M_construct<char*> (basic_string.h:214) by 0x402442: basic_string (basic_string.h:401) by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:172) by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:184) by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:217) by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:237) by 0x402442: pre (attributes.hpp:23)
是的,这是一个 QoI 问题。
问题通常出在 c++ 的隐式转换上。指针类型有许多意想不到的转换。共享指针确实将它们的上下文转换为 bool。
更多注释:
您的融合改编似乎有缺陷:
中使用val
未在 setterBOOST_FUSION_ADAPT_ADT(DataElement, (std::string &, const std::string &, obj.str(), obj.str() = val))
你正在做很多我学会避免的事情。
- 我更喜欢无语义动作的规则:Boost Spirit: "Semantic actions are evil"?
- 我不在 Spirit 中使用共享指针 grammars/generators How can I use polymorphic attributes with boost::spirit::qi parsers?(可以说,在生成器设置中问题不大!)
- 我不做ADT适配(UB太容易被咬了)