Adapt class containing a string member as synthesized attribute

boost-fusion, boost-spirit, c++

Solution

As you have seen from the linked dupe, you can get around this with a well-placed `qi::eps`.

    start = qi::eps >> qi::lexeme[+qi::char_("a-z")];

See it Live On Coliru

However, there are cases where `qi::eps` doesn't save. (Will try to find links later). Therefore, I've come to favour the 'old-fashioned approach' for attribute compatibility:

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

struct symbol 
{ 
    std::string repr; 

    symbol(std::string repr = std::string()) : repr(std::move(repr)) {}
};

namespace qi = boost::spirit::qi;

template <typename Iterator>
struct test_grammar : qi::grammar<Iterator, symbol(), qi::ascii::space_type> {
    test_grammar() : test_grammar::base_type(start) {
        using namespace qi;

        start = as_string[ lexeme[ +char_("a-z") ] ];
    }

    qi::rule<Iterator, symbol(), qi::ascii::space_type> start;
};

#include <iostream>

auto main() -> int {
    test_grammar<std::string::iterator> grammar{};
    auto input = std::string{"test"};
    auto output = symbol{};
    auto e = end(input);
    if (qi::phrase_parse(begin(input), e, grammar, qi::ascii::space, output))
        std::cout << output.repr;
}

This is also likely a bit lighter on the compiler. See it Live on Coliru as well.

If all else fails you can have your cake and eat it too because attribute transformation/assignment are customization points in the library.

- Attribute Propagation and Attribute Compatibility

- Store a Parsed Attribute Value (Qi)

- Store Parsed Attribute Values into a Container (Qi)

- search for these traits in answers on SO for good examples

Problem

I’m trying to parse a character string into an attribute of a custom type `symbol`, which contains a `std::string` member. I thought I could use `BOOST_FUSION_ADAPT_STRUCT` here but that doesn’t work. If I declare the rule as `rule<It, std::string(), space_type>` it works. If I define it as `rule<It, symbol(), space_type>` it fails with the error “no type name `value_type` in `symbol`”. I think Spirit is trying to append the value character-for-character to the attribute, which fails as expected. But isn’t there a way of making this work, without adding an additional intermediate rule which captures the `std::string` attribute? Here’s the full MWE: ``` #include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix_fusion.hpp> struct symbol { std::string repr; }; BOOST_FUSION_ADAPT_STRUCT(symbol, (std::string, repr)) namespace qi = boost::spirit::qi; template <typename Iterator> struct test_grammar : qi::grammar<Iterator, symbol(), qi::ascii::space_type> { test_grammar() : test_grammar::base_type{start} { start = qi::lexeme[+qi::char_("a-z")]; } qi::rule<Iterator, symbol(), qi::ascii::space_type> start; }; #include <iostream> auto main() -> int { test_grammar<std::string::iterator> grammar{}; auto input = std::string{"test"}; auto output = symbol{}; auto e = end(input); if (qi::phrase_parse(begin(input), e, grammar, qi::ascii::space, output)) std::cout << output.repr; } ```

Original source