nam*_*ero 9 c++ boost-spirit boost-fusion boost-spirit-qi
我有一个关于Spirit Qi的编译问题,它抱怨value_type不是标识符的成员.出于某种原因,Qi的属性系统将标识符视为容器类型,并尝试枚举它的值类型.
这是与此问题类似的问题,但是,我认为原因是单个成员结构,可能与此错误有关.
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace boost::spirit::qi;
struct identifier
{
std::wstring name;
};
struct problem
{
identifier _1;
identifier _2;
identifier _3;
};
BOOST_FUSION_ADAPT_STRUCT(
identifier,
(std::wstring, name)
)
BOOST_FUSION_ADAPT_STRUCT(
problem,
(identifier, _1)
(identifier, _2)
(identifier, _3)
)
int main(int argc, char* argv[])
{
rule<std::wstring::const_iterator, identifier()> gr_identifier = eps >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]];
// Ok, compiles
/*rule<std::wstring::const_iterator, problem()> gr_problem = gr_identifier
>> gr_identifier
>> '('
>> gr_identifier
>> ')';*/
// Fails
rule<std::wstring::const_iterator, problem()> gr_problem = gr_identifier
>> gr_identifier
>> '('
> gr_identifier
> ')';
std::wstring input = L"foo goo(hoo)";
/*bool dummy = phrase_parse(
input.begin(), input.end(),
gr_problem,
space);*/
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,这仅在使用期望解析器时发生(请参阅示例中的定义2).定义1,仅使用序列解析器,正确编译(并执行).
有人知道这个的正确解决方案吗?
另请查看实例
seh*_*ehe 15
这是一个非常臭名昭着的精神边缘案例.问题是,Spirit 中单元素Fusion序列的特殊情况处理打破了一些抽象.
通常的解决方法是使暴露属性方面不那么简单:
rule<It, single_member_struct()> r = eps >> XXX;
// the `eps` is there to break the spell
Run Code Online (Sandbox Code Playgroud)
但是,这里不起作用,因为你的(a > XXX > b)子表达式会产生另一个,vector1<decltype(member_type)>而这次,没有多少智能括号或eps-ing会为你节省.[1]
总而言之,我有三个解决方法:
你只gr_identifier需要返回一个std::wstring[2]:
rule<It, std::string()> gr_identifier =
(alpha | '_') >> *(alnum | '_');
Run Code Online (Sandbox Code Playgroud)
这实际上只是推迟了使用Fusion改编的魔法属性转换,identifier从而打破了咒语:
rule<It, problem(), qi::space_type> gr_problem =
gr_identifier
>> gr_identifier
>> ('(' > gr_identifier > ')')
;
Run Code Online (Sandbox Code Playgroud)
只是工作.我认为这可能是最不具侵入性的解决方法
因此,你可以通过......使identifier结构不融合成单元素融合序列.是.这涉及添加虚拟场的EvilHack™.为了尽量减少混淆,让我们尽可能地做到qi::unused_type:
struct identifier
{
std::string name;
qi::unused_type dummy;
};
BOOST_FUSION_ADAPT_STRUCT(
identifier,
(std::string, name)
(qi::unused_type, dummy)
)
Run Code Online (Sandbox Code Playgroud)
现在:
rule<It, identifier()> gr_identifier =
(alpha | '_') >> *(alnum | '_') >> attr(42); // that's hacky
Run Code Online (Sandbox Code Playgroud)
作品
最后的解决方法可能是最明显的:首先不要将结构作为融合序列进行调整,并获利:
struct identifier
{
std::string name;
identifier() = default;
explicit identifier(std::string name)
: name(std::move(name))
{}
};
Run Code Online (Sandbox Code Playgroud)
请注意,要允许属性传播,现在需要使用合适的转换构造函数.此外,Spirit中的公开属性需要默认构造函数.
现在,
rule<It, identifier()> gr_identifier =
as_string [ (alpha | '_') >> *(alnum | '_') ]; // cleaner... but no fusion
Run Code Online (Sandbox Code Playgroud)
作品.如果您不需要将此类型的Fusion用于其他目的,这可能会更直观.
注意:这个变体在编译时可能是最有效的
我认为对于你的代码,有2个完全可行的解决方法(#1和#3),还有一个不太常见(具有虚拟字段的解决方案),但我将其包含在纪录片中.
备查
#define BOOST_SPIRIT_DEBUG
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;
//////////////////////////////////////////
// Select workaround to demonstrate
#define KEEP_STRING_WORKAROUND
// #define DUMMY_WORKAROUND™
// #define NO_ADAPT_WORKAROUND
//////////////////////////////////////////
#if defined(KEEP_STRING_WORKAROUND)
struct identifier
{
std::string name;
};
BOOST_FUSION_ADAPT_STRUCT(
identifier,
(std::string, name)
)
#elif defined(DUMMY_WORKAROUND)
struct identifier
{
std::string name;
qi::unused_type dummy;
};
BOOST_FUSION_ADAPT_STRUCT(
identifier,
(std::string, name)
(qi::unused_type, dummy)
)
#elif defined(NO_ADAPT_WORKAROUND)
struct identifier
{
std::string name;
identifier() = default;
explicit identifier(std::string name)
: name(std::move(name))
{}
};
#endif
struct problem
{
identifier _1;
identifier _2;
identifier _3;
};
BOOST_FUSION_ADAPT_STRUCT(
problem,
(identifier, _1)
(identifier, _2)
(identifier, _3)
)
//////////////////////////////////////////
// For BOOST_SPIRIT_DEBUG only:
static inline std::ostream& operator<<(std::ostream& os, identifier const& id) {
return os << id.name;
}
//////////////////////////////////////////
int main()
{
using namespace qi;
typedef std::string::const_iterator It;
#if defined(KEEP_STRING_WORKAROUND)
rule<It, std::string()> gr_identifier =
(alpha | '_') >> *(alnum | '_');
#elif defined(DUMMY_WORKAROUND)
rule<It, identifier()> gr_identifier =
(alpha | '_') >> *(alnum | '_') >> attr(42); // that's hacky
#elif defined(NO_ADAPT_WORKAROUND)
rule<It, identifier()> gr_identifier =
as_string [ (alpha | '_') >> *(alnum | '_') ]; // cleaner... but no fusion
#endif
rule<It, problem(), qi::space_type> gr_problem =
gr_identifier
>> gr_identifier
>> ('(' > gr_identifier > ')')
;
std::string input = "foo goo(hoo)";
BOOST_SPIRIT_DEBUG_NODES((gr_problem)(gr_identifier));
It f(begin(input)), l(end(input));
bool dummy = phrase_parse(f, l, gr_problem, qi::space);
return dummy? 0 : 255;
}
Run Code Online (Sandbox Code Playgroud)
[1]相信我,我试过,即使插入qi::unused_type"假"属性,和/或使用attr_cast<>或帮助规则来强制子表达式的类型.
[2]出于演示目的,我使用了std::string因为我认为它与BOOST_SPIRIT_DEBUG混合得更好