Boost Spirit X3 cannot compile repeated variable directive

I am trying to use the Boost Spirit X3 repeat directive with a repeat variable. The basic idea is that the header + payload, where the header indicates the size of the payload. The simple example "3 1 2 3" is interpreted as header = 3, data = {1, 2, 3} (3 integers).

I could only find examples from the qi spirit documentation. It uses the boost phoenix link to wrap the variable factor: http://www.boost.org/doc/libs/1_50_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html

std::string str; int n; test_parser_attr("\x0bHello World", char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str); std::cout << n << ',' << str << std::endl; // will print "11,Hello World" 

I wrote the following simple example for the x3 spirit with no luck:

 #include <boost/spirit/home/x3.hpp> #include <boost/spirit/include/phoenix.hpp> #include <string> #include <iostream> namespace x3 = boost::spirit::x3; using x3::uint_; using x3::int_; using x3::phrase_parse; using x3::repeat; using x3::space; using std::string; using std::cout; using std::endl; int main( int argc, char **argv ) { string data("3 1 2 3"); string::iterator begin = data.begin(); string::iterator end = data.end(); unsigned int n = 0; auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); }; bool r = phrase_parse( begin, end, uint_[f] >> repeat(boost::phoenix::ref(n))[int_], space ); if ( r && begin == end ) cout << "Parse success!" << endl; else cout << "Parse failed, remaining: " << string(begin,end) << endl; return 0; } 

Compiling the code above with boost 1.59.0 and clang ++ (flags: -std = C ++ 14) gives the following:

 boost_1_59_0/boost/spirit/home/x3/directive/repeat.hpp:72:47: error: no matching constructor for initialization of 'proto_child0' (aka 'boost::reference_wrapper<unsigned int>') typename RepeatCountLimit::type i{}; 

If I hardcode repeat(3) instead of repeat(boost::phoenix::ref(n)) , it works correctly, but this is not a possible solution, since it should support a variable repetition rate.

Compilation with repeat(n) succeeds, but the following output cannot be parsed: "Parse failed, remaining: 1 2 3"

Looking at the source code for boost/spirit/home/x3/directive/repeat.hpp:72 , it calls the empty constructor for the pattern type RepeatCountLimit::type variable i , and then iterates through the for loop to iterate over min and max. However, since the type is a reference, it must be initialized in the constructor, so compilation is not performed. Considering the equivalent source code from the previous version of the boost / spirit / home / qi / directive / repeat.hpp: 162 library, it is assigned directly:

  typename LoopIter::type i = iter.start(); 

I'm not sure what I'm doing wrong here, or if x3 does not currently support variable repeat rates. I would be grateful for the help in solving this problem. Thanks.

+8
c ++ boost-spirit boost-spirit-x3
source share
1 answer

From what I compile by reading the source and the mailing list, Phoenix does not integrate into X3 at all: the reason is that C ++ 14 makes most of them obsolete.

I agree that this leaves a few places where Qi used elegant solutions, for example. eps(DEFERRED_CONDITION) , lazy(*RULE_PTR) ( Nabialek trick ), and indeed, this case.

Spirit X3 is still under development, so we can see this added number <

Spirit X3 currently has one generic stateful context tool. This essentially replaces locals<> , in some cases inherited arguments, and the number of elements can be / done / checked in this particular case:

  • x3::with ²

Here you can use it:

 with<_n>(std::ref(n)) [ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ] 

Here _n is the type of tag that identifies the context element to search with get<_n>(cxtx) .

Note that we must currently use the reference wrapper for lvalue n , because with<_n>(0u) will result in a constant element within the context. I believe this is also a QoI that can be removed when X # matures

Now, for semantic actions:

 unsigned n; struct _n{}; auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); }; 

It stores the parsed unsigned number in the context. (In fact, due to the ref(n) binding, it is not actually part of the context at the moment, as mentioned)

 auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); }; 

Here we check that we are not really "complete", i.e. more integers allowed

 auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); }; 

Here we check that we are "complete", i.e. more integers are not allowed.

Putting it all together:

Live on coliru

 #include <string> #include <iostream> #include <iomanip> #include <boost/spirit/home/x3.hpp> int main() { for (std::string const input : { "3 1 2 3", // correct "4 1 2 3", // too few "2 1 2 3", // too many // " 3 1 2 3 ", }) { std::cout << "\nParsing " << std::left << std::setw(20) << ("'" + input + "':"); std::vector<int> v; bool ok; { using namespace boost::spirit::x3; unsigned n; struct _n{}; auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); }; auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); }; auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); }; auto r = rule<struct _r, std::vector<int> > {} %= with<_n>(std::ref(n)) [ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ]; ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v); } if (ok) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " ")); } else { std::cout << "Parse failed"; } } } 

What prints:

 Parsing '3 1 2 3': 3 elements: 1 2 3 Parsing '4 1 2 3': Parse failed Parsing '2 1 2 3': Parse failed Parsing ' 3 1 2 3 ': 3 elements: 1 2 3 

¹ provide your support / vote on the [spirit-general] mailing list :)

² cannot find a suitable link for documentation, but it is used in some samples

+7
source share

All Articles