灵气属性传播问题与单成员结构.
Spirit Qi attribute propagation issue with single-member struct
我对灵气有一个编译问题,它抱怨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;
}
有趣的是,这仅在使用期望解析器时发生(请参阅示例中的定义 2)。定义 1,仅使用序列解析器,正确编译(和执行)。
有谁知道正确的解决方案吗?
另请查看现场示例
这是Spirit中一个非常臭名昭著的边缘情况。问题是,Spirit中对单元素融合序列的特殊情况处理打破了一些抽象。
通常的解决方法是将公开属性端调整为不那么简单:
rule<It, single_member_struct()> r = eps >> XXX;
// the `eps` is there to break the spell
但是,在这里这是行不通的,因为您的(a > XXX > b)
子表达式会导致另一个vector1<decltype(member_type)>
,而这一次,再多的智能括号或eps
-ing 都无法拯救您。[1]
长话短说,我有三种解决方法:
1. #define KEEP_STRING_WORKAROUND
在科里鲁现场观看
在其中,您只需允许gr_identifier
返回std::wstring
[2]:
rule<It, std::string()> gr_identifier =
(alpha | '_') >> *(alnum | '_');
这实际上只是推迟了使用融合改编的魔法属性转换(如果identifier
),从而打破了魔咒:
rule<It, problem(), qi::space_type> gr_problem =
gr_identifier
>> gr_identifier
>> ('(' > gr_identifier > ')')
;
只是工作。我认为这可能是侵入性最小的解决方法
2. #define DUMMY_WORKAROUND
在科里鲁现场观看
借此你解开魔术师...使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)
)
现在:
rule<It, identifier()> gr_identifier =
(alpha | '_') >> *(alnum | '_') >> attr(42); // that's hacky
工程
3. #define NO_ADAPT_WORKAROUND
在科里鲁现场观看
最后的解决方法可能是最明显的:首先不要将结构调整为融合序列,并获利:
struct identifier
{
std::string name;
identifier() = default;
explicit identifier(std::string name)
: name(std::move(name))
{}
};
请注意,要允许属性传播,现在您需要存在合适的转换构造函数。此外,Spirit中公开的属性需要默认构造函数。
现在
rule<It, identifier()> gr_identifier =
as_string [ (alpha | '_') >> *(alnum | '_') ]; // cleaner... but no fusion
工程。如果您不需要该类型的 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;
}
[1] 相信我,我尝试过,即使在插入qi::unused_type
"假"属性和/或使用attr_cast<>
或帮助程序规则来强制子表达式的类型时也是如此。
[2] 出于演示目的,我使用了std::string
,因为我相信它与BOOST_SPIRIT_DEBUG混合得更好
- 如何在C++中定义静态成员结构
- 使用模板化类的成员结构
- 初始化固定的 C 数组成员结构
- 指向成员结构的指针如何工作
- 如何将此“指针从外部类传递到成员结构
- 导致IAR ARM中出现错误的成员结构位字段元素的Initializer列表初始化
- 映射文件中成员结构的地址
- Visual C++ 模板类成员结构初始化语法糖
- C :初始化成员结构的静态字段的正确方法
- 如何在C 类的初始化器列表中使用未命名结构初始化成员结构
- 从成员结构的成员函数中访问类的成员?
- 模板化类的成员函数无法返回指向成员结构的指针?
- 重载非类型模板结构的成员结构的复制赋值运算符
- 灵气属性传播问题与单成员结构.
- 将具有 int* 成员C++结构编接到 C#
- 复杂的班级成员结构
- 将向量成员(结构)传递给函数
- 成员结构的前向声明
- 重载运算符<<用于模板结构中定义的成员结构
- 返回带有模板化类的类成员结构