Boost::spirit passing semantic actions in inherited attributes

纵然是瞬间 提交于 2019-12-12 15:08:43

问题


I'm trying to pass semantic action in a grammar's inherited argument.

In the very basic example below the grammar parses two numbers and I pass semantic action (in a form of c++ lambda) into it and I'd like this action to be called on parsing of the first number. However it does not called but silently ignored and I'd like to know why is it so and what is the proper way to do such things.

#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 expected output is:

the meaning of life is 42
parse ok

And the real output is:

parse ok

回答1:


First, immediate, response:

"I'm trying to pass semantic action in a grammar's inherited argument."

instant traumatic shock. You... you... what?!

C++ is not very good for higher order programming, certainly not with static polymorphism based on expression templates. In fact it is, but in my previous answer I already cautioned against UB when storing expression templates in named objects (≅ variables).

That time around it was UB that you spotted. That's lucky, in my opinion.

Recently, I've already encountered another question about similar goals:

  • Constructing a qi::rule with a function attribute

Pay special attention to the comment thread. I don't think this is a sane path, at least not until Boost Mpl has been done with full C++11 goodness (Boost Hana, perhaps?) and Proto-0x is released.

By then, Spirit X3 is probably mature, and we're just left with the gap of Boost Phoenix. I'm not sure whether that's on anyones agenda.

In short: we'll be stuck in this "halfway" land where we can have nice things, but with some pretty constrained restrictions. We should probably avoid getting carried away and pretending we're suddenly capable of writing Haskell in C++.

Also relevant: There's a proposal (N4221, pdf) for Generalized Lifetime Extension of References in C++. It comes with some good examples of simple applications of say Boost Range adaptors that are silently UB in current C++. E.g. from §2.3 Universal observation:

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

Solution

That said, since the inherited argument would be a functor (not a lazy actor), you need to bind it:

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

That does work: Live On Coliru

However, I don't recommend this



来源:https://stackoverflow.com/questions/26414677/boostspirit-passing-semantic-actions-in-inherited-attributes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!