Search code examples
c++boostboost-spirit

Boost Spirit semantic actions on non-Fusion adapted structs


Say if I have a simple struct like:

struct huh { char xxx; };  

Without going to Boost Fusion adapted structs, I'd like to find a simpler proxy for operating on a member variable for in a Spirit rule's semantic action. Now this, of course, doesn't work:

_val.xxx = _1

but I'd like to get a little cleaner than binding with phoenix:

bind(&huh::xxx, _val) =  _1 

Here is a working example of the simple picture that compiles on a clang and a g++:

#define BOOST_SPIRIT_USE_PHOENIX_V3 1
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/phoenix/bind/bind_member_variable.hpp>

struct huh { char xxx; };

int main() {
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phx   = boost::phoenix;

    qi::rule<std::string::const_iterator, huh(), ascii::space_type> start;

    start =  qi::char_[
        phx::bind(&huh::xxx, qi::_val) =  qi::_1 
    ];

    return 0;
}

Any ideas?


Solution

  • Binding with fusion is exactly the glue to make it "a little cleaner than binding with phoenix".

    So, there's your solution. Alternatively, factor your expression templates, maybe:

    auto _xxx = phx::bind(&huh::xxx, _val);
    

    So you can write:

    start =  char_[ _xxx = _1 ];
    

    Alternatively, use a function or a customization point:

    Customization point

    Adding a trait

    namespace boost { namespace spirit { namespace traits {
    
        template <> struct assign_to_attribute_from_value<huh, char, void> {
            static void call(char c, huh& attr) { attr.xxx = c; }
        };
    
    } } }
    

    You can now simply write

    qi::rule<std::string::const_iterator, huh(), ascii::space_type> start;
    
    start = qi::char_;
    

    Live On Coliru

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/spirit/include/qi.hpp>
    
    struct huh { char xxx; };
    
    namespace boost { namespace spirit { namespace traits {
    
        template <> struct assign_to_attribute_from_value<huh, char, void> {
            static void call(char c, huh& attr) { attr.xxx = c; }
        };
    
    } } }
    
    int main() {
        namespace qi    = boost::spirit::qi;
        namespace ascii = boost::spirit::ascii;
    
        qi::rule<std::string::const_iterator, huh(), ascii::space_type> start;
    
        start = qi::char_;
    }