【问题标题】:Boost::spirit passing semantic actions in inherited attributesBoost::spirit 在继承的属性中传递语义动作
【发布时间】:2014-10-17 06:51:27
【问题描述】:

我试图在语法的继承参数中传递语义动作。

在下面的非常基本的示例中,语法解析两个数字,我将语义操作(以 c++ lambda 的形式)传递给它,我希望在解析第一个数字时调用此操作。但是它没有调用,而是默默地忽略了,我想知道为什么会这样以及做这些事情的正确方法是什么。

#include <iostream>
#include <boost/spirit/include/qi.hpp>

using namespace std;
using namespace boost;

namespace qi = spirit::qi;
namespace phx = phoenix;

template <typename Iterator, typename Action>
struct two_numbers : qi::grammar<Iterator, void (Action const&)>
{
  two_numbers() : two_numbers::base_type(start)
  {
    using namespace qi;
    start = int_ [ _r1 ] >> ' ' >> int_;
  }
  qi::rule<Iterator, void (Action const&)> start;
};

int main ()
{
  string input { "42 21" };
  auto first=std::begin (input), last=std::end(input);

  static const auto my_action = [] (auto&& p) {
    cout << "the meaning of life is " << p << "\n";
  };

  static const two_numbers <decltype(first), decltype (my_action)> p;

  if (qi::parse (first, last, p(phx::ref(my_action))))
    cout << "parse ok\n";
}

预期的输出是:

the meaning of life is 42
parse ok

而真正的输出是:

parse ok

【问题讨论】:

标签: c++ parsing boost grammar boost-spirit


【解决方案1】:

首先,立即响应:

“我试图在语法的继承参数中传递语义动作。”

瞬间创伤性休克。你……你……什么?!

C++ 不太适合高阶编程,当然也不适用于基于表达式模板的静态多态性。事实上确实如此,但在我之前的回答中,我已经警告过在命名对象(≅ 变量)中存储表达式模板时要注意 UB。

你发现的那个时候是UB。在我看来,这很幸运。

最近,我已经遇到了另一个关于类似目标的问题:

特别注意评论线程。我不认为这是一条理智的道路,至少在 Boost Mpl 完成了完整的 C++11 之后(Boost Hana,也许?)并发布了Proto-0x

到那时,Spirit X3 大概已经成熟了,我们只剩下 Boost Phoenix 的差距了。我不确定这是否在任何人的议程上。

简而言之:我们将被困在这个“半途而废”的土地上,在那里我们可以拥有美好的事物,但受到一些非常有限的限制。我们可能应该避免得意忘形,假装我们突然能够用 C++ 编写 Haskell。

同样相关:有一个提案 (N4221, pdf) 用于 C++ 中引用的通用生命周期扩展。它附带了一些简单应用的好例子,比如在当前 C++ 中默默 UB 的 Boost Range 适配器。例如。来自§2.3 普遍观察:

std::vector<int> vec;
for (int val : vec | boost::adaptors::reversed
                   | boost::adaptors::uniqued) 
{
       // Error: result of (vec | boost::adaptors::reversed) died.
}

解决方案

也就是说,由于继承的参数是一个仿函数(不是一个惰性actor),你需要绑定它:

    start = int_ [ phx::bind(phx::cref(_r1), qi::_1) ] >> ' ' >> int_;

确实有效:Live On Coliru

但是,我不推荐这个

【讨论】:

  • 添加了 N4221 提案的链接(感谢 @AndyProwl)
  • 感谢您的回答。我修改了我的示例,现在传递了惰性actor,但它仍然无法按预期工作:COLIRU Link 另外,如果由于早期破坏的prvalue和悬空引用而发生未定义的行为,您能否在代码?
  • @NikkiChumakov 我不能(目前)。而且我可能找不到动机。然而,在 Boost Phoenix 中有 的东西,它告诉了 Phoenix 表达式模板是否是“无状态的”(如果表达式的类型是默认可构造的,我可能记得一半是这种情况?)。如果是这种情况,那么存储它就没有问题。否则,所有赌注都取消。 Eric Niebler 本人有一篇帖子详细说明了这一点,IIRC。
  • 这可能是我记得的帖子:Static functions from boost.lambda or boost.phoenix。那里的目标略有不同,所以你必须看透它,看看它是如何在这里应用的。
  • 但是我的静态 lambda 引用与我可能想要传入语法的继承属性的任何其他对象之间有什么区别呢?在这里真正对 UB 负责的是什么——我的代码?凤凰?原型? C++?是的,情况与 range::adaptors 非常相似,当规范中没有任何内容让我们认为在管道中组合多个适配器是不可能的。无论如何,感谢您提供有趣的阅读列表,例如 Boost.Hana 以及您所指的提案和其他帖子。真的很有趣。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多