Search code examples
c++boostboost-spiritboost-spirit-qiboost-phoenix

How to access boost::variant members from Spirit::Qi rule?


I can't find a proper way how to access members of boost::variant using boost::phoenix in my Spirit-Qi grammar. Here is simple an example what I’m trying to achieve. (my whole grammar is much more complex, this is simple fragment where I'm testing mentioned problem).

namespace ph = boost::phoenix;
typedef boost::variant<std::string,int> VariantType;
typedef std::list<VariantType> TlstVariants;

rule<Iterator, void(TlstVariants&), Skipper>    rule1;

rule1 = 
   qi::eps [ ph::push_back(qi::_r1, ph::construct<int>(2)) ]
>> qi::eps [ ph::get<int>(ph::back(qi::_r1)) = ph::val(3) ] //THIS IS EXAMPLE OF WHAT I NEED
;

TlstVariants lstVals;
ExecuteParser("5",rule1( ph::ref(lstVals) ));   

BOOST_FOREACH( VariantType &val, lstVals )
{
    std::cout << val.which() << " - " << val;
}

But I can't find any phoenix::get<> or any similar method to access boost::variant using Phoenix. Reason why I need phoenix::get<> is because I need to insert variant to list with specific type and then pass this specific type as reference to the child rule as inherited attribute:

qi::rule<Iterator, void(structTest&), Skipper> rule_child;

rule = 
  qi::lit("test") [ph::push_back(sp::_r1, ph::construct<structTest>())]
> qi::lit('(') 
> rule_child( ph::get<structTest>(ph::back(sp::_r1)) ) 
> qi::lit(')') 
...

Is there any way how to achieve behavior like this?

Thank for any reply

Rick


Solution

  • It's fairly easy to write your own 'lazy' Phoenix function. Here is one for boost::variant.

    #include <boost/variant.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    template <typename Result>
    struct get_impl
    {
        template <typename T>
        struct result
        {
            typedef Result type;
        };
    
        template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
        Result operator()(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& v) const
        {
            return boost::get<Result>(v);
        }
    };
    
    ph::function<get_impl<int> > const get_int = get_impl<int>();
    

    Now, this can be used in a semantic action:

    ... qi::eps [ get_int(ph::back(qi::_r1)) = ph::val(3) ]