8

This code does not compiles (gcc 5.3.1 + boost 1.60):

#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;

template <typename T>
void parse(T begin, T end) {
    auto dest = x3::lit('[') >> x3::int_ >> ';' >> x3::int_ >> ']';

    auto on_portal = [&](auto& ctx) {};
    auto portal    = (x3::char_('P') >> -dest)[on_portal];

    auto tiles = +portal;
    x3::phrase_parse(begin, end, tiles, x3::eol);
}

int main() {
    std::string x;
    parse(x.begin(), x.end());
}

It fails with a static assertion:

error: static assertion failed: Attribute does not have the expected size.

Thanks to wandbox I also tryied boost 1.61 and clang, both produce the same results.

If I remove the semantic action attached to portal, it compiles fine; the same happens if I change dest to:

auto dest = x3::lit('[') >> x3::int_ >> ']';

Any help would be appreciated. TIA.

dvd
  • 1,014
  • 6
  • 12
  • 1
    This reproducer is excellent. I love when people narrow down the problem to the essence. +10 if I could – sehe Jul 05 '16 at 23:15

2 Answers2

4

This is surprising to me too, I'd report it at the mailing list (or the bug tracker) as a potential bug.

Meanwhile, you can "fix" it by supplying an attribute type for dest:

Live On Coliru

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

template <typename T>
void parse(T begin, T end) {
    auto dest = x3::rule<struct dest_type, std::tuple<int, int> > {} = '[' >> x3::int_ >> ';' >> x3::int_ >> ']';

    auto on_portal = [&](auto& ctx) {
        int a, b;
        if (auto tup = x3::_attr(ctx)) {
            std::tie(a, b) = *tup;
            std::cout << "Parsed [" << a << ", " << b << "]\n";
        }
    };
    auto portal    = ('P' >> -dest)[on_portal];

    auto tiles = +portal;
    x3::phrase_parse(begin, end, tiles, x3::eol);
}

int main() {
    std::string x = "P[1;2]P[3;4]P[5;6]";
    parse(x.begin(), x.end());
}

Prints:

Parsed [1, 2]
Parsed [3, 4]
Parsed [5, 6]

NOTE I changed char_('P') into just lit('P') because I didn't want to complicate the sample dealing with the character in the attribute. Perhaps you didn't mean to have it in the exposed attribute anyways.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Is there any overhead of having to set up the grammar variables (like `tiles`) every time `parse()` is being called? Would it be better to have a `class parse` with (perhaps `static`) members that can be initalized once and then reuse them on multiple calls? It would also make it easier to provide your own parsers in header-only code. – TemplateRex Aug 06 '16 at 12:09
  • I don't think in the majority of code it matters, because everything can be inlined. `tiles` will probably never even exist. If you want to use client-supplied parsers, then you - by definition - desire type erasure. That's `x3::any_parser` territory IYAM. If you just want to logically group rules (hiding everything but the starting point) then I'd simply return it from a function. X3 has no problem with that, as opposed to Qi – sehe Aug 12 '16 at 21:27
  • This still does not work today (Boost 1.64). Was it ever reported as a bug? – akim Jan 14 '18 at 10:27
0

The bug is fixed in Boost 1.77 (by PR665 X3: Optional parser is not a passthrough parser).

It might be confusing why the fix applies in this case which is attribute-less, but it actually isn't, semantic actions create a temporary attribute for you to be able to access via context (even when you are really don't need it).

Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30