Spirit X3, semantic action makes compilation fails with: Attribute does not have the expected size
Asked Answered
A

2

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.

Accroach answered 5/7, 2016 at 22:16 Comment(1)
This reproducer is excellent. I love when people narrow down the problem to the essence. +10 if I couldShipway
S
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.

Shipway answered 5/7, 2016 at 23:14 Comment(3)
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.Magdalen
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 QiShipway
This still does not work today (Boost 1.64). Was it ever reported as a bug?Nebuchadnezzar
V
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).

Visionary answered 7/9, 2022 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.