【问题标题】:Boost.Spirit.Qi: Take a rule's attribute and set it as a field of an enclosing rule's struct attribute?Boost.Spirit.Qi:获取规则的属性并将其设置为封闭规则的结构属性的字段?
【发布时间】:2011-06-04 22:28:09
【问题描述】:

就像许多其他问题一样,我正在尝试使用 Boost.Spirit.Qi 将简单的语法解析为结构树。

我将尝试将我正在尝试做的事情提炼为最简单的情况。我有:

struct Integer {
  int value;
};
BOOST_FUSION_ADAPT_STRUCT(Integer, (int, value))

稍后,在语法结构内部,我有以下成员变量:

qi::rule<Iterator, Integer> integer;

我用它来定义

integer = qi::int_;

但是,当我尝试实际解析一个整数时,使用

qi::phrase_parse(iter, end, g, space, myInteger);

myInteger.value 在成功解析后总是未初始化。 同样,我也尝试过以下定义(显然不编译的都是错误的):

integer = qi::int_[qi::_val = qi::_1]; //compiles, uninitialized value
integer = qi::int_[qi::_r1 = qi::_1]; //doesn't compile
integer = qi::int_[phoenix::bind(&Integer::value, qi::_val) = qi::_1]; //doesn't
integer = qi::int_[phoenix::at_c<0>(qi::_val) = qi::_1]; //doesn't

很明显,我对 Spirit、Phoenix 或其他东西有误解。我的理解是qi::_1qi::int_的第一个属性,在这里,应该代表解析的整数,当方括号中的部分作为函数对象执行时。然后我假设函数对象将采用封闭的integer 属性qi::_val 并尝试将解析的整数分配给它。我的猜测是,由于我的BOOST_FUSION_ADAPT_STRUCT 调用,这两者将是兼容的,从静态分析的角度来看确实是这样,但数据并没有被保留。

是否有我在某处遗漏的参考 (&) 名称?

【问题讨论】:

  • 我刚刚找到了另一个可以编译的组合,尽管它不会产生初始化数据:我向Integer 添加了一个构造函数,它接受value 的值,然后定义了我的integer解析器为integer = qi::long_long[qi::_val = phx::construct&lt;Integer&gt;(qi::_1)];
  • 更多调试说明。在我的完整代码中,我实际上有qi::rule&lt;Iterator, Integer, ascii::space_type&gt; integer;,如果我将Integer 替换为Integer(),它看起来会中断,并且所有示例都有尾随(),我忽略了它。因此,rule 的模板参数可能被搞砸了。挖掘。

标签: c++ boost-spirit boost-spirit-qi boost-phoenix


【解决方案1】:

如果Integer应该是规则暴露的属性,则需要声明为:

qi::rule<Iterator, Integer()> integer; 

(注意括号)。 Spirit 要求使用函数声明语法来描述规则的“接口”。它不仅在 Spirit 中使用,还被其他几个库使用(例如,参见 boost::function)。

这样做的主要原因是它是指定函数接口的一种简洁的方式。如果您考虑一下规则是什么,您很快就会意识到它就像一个函数:它可能会返回一个值(解析结果,即合成属性)。此外,它可能需要一个或多个参数(继承的属性)。

第二个但次要的原因是 Spirit 需要能够区分规则的不同模板参数。模板参数可以按任何顺序指定(迭代器除外),因此它需要一些方法来确定什么是什么。函数声明语法与跳过程序或编码(其他两个可能的模板参数)有很大不同,可以在编译时识别它。

让我们看看你的不同尝试:

如果您按上述方式更改规则定义,则可以使用此功能。

integer = qi::int_[qi::_val = qi::_1]; 

_val 指的是您的Integer,而_1 指的是int。因此,您需要从int 定义一个赋值运算符来完成这项工作:

struct Integer {
    int value;
    Integer& operator=(int) {...}
};                    

在这种情况下,您无需将类型调整为 Fusion 序列。

但是你可以写得更简单:

integer = qi::int_ >> qi::eps;

这是 100% 等效的(eps 是一种用于将右侧转换为解析器序列的技巧,它允许利用内置的属性传播将适应的 Fusion 序列的元素映射到元素的属性序列)。

这个:

integer = qi::int_[qi::_r1 = qi::_1]; 

将不起作用,因为_r1 指的是规则的第一个继承属性。但是,您的规则没有继承属性。

这将起作用:

integer = qi::int_[phoenix::bind(&Integer::value, qi::_val) = qi::_1];

它不需要将您的类型调整为 Fusion 序列。

这也可以:

integer = qi::int_[phoenix::at_c<0>(qi::_val) = qi::_1]; 

【讨论】:

  • 哦,漂亮,谢谢。所以看起来我一直都是模板错字。
  • 非常好的答案。我个人认为对 boost 标签的支持应该值得 +20。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-07
  • 2014-09-27
  • 2013-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多