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

I cannot find the correct way to access boost :: variant members using boost :: phoenix in my Spirit-Qi grammar. Here is a simple example of what I'm trying to achieve. (my whole grammar is much more complicated, this is a simple snippet where I am testing the 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 not find phoenix :: get <> or any similar method to access boost :: variant using Phoenix. The reason I need phoenix :: get <> is because I need to insert an option into a list with a specific type, and then pass that specific type as a reference to the child rule as an 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 a way to achieve this behavior?

Thanks for any answer

Rick

+4
source share
2 answers

It's pretty 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 it can be used in semantic action:

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

I think I found a way to do this. (I don’t know, this is the best way, but it works ;-)). The problem was int & type, because boost :: variant contains int, not int &. Therefore, I am updating your template to accept two types: one for the getter option and one for the return type.

I updated the get_impl template as follows:

 template <typename Result, typename Inner> 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)> & v) const { return boost::get<Inner>(v); } }; 

And my grammar now looks like this:

 typedef boost::variant<std::string,int> VariantType; qi::rule<DG_Iterator, void(VariantType&), DG_Skipper> rule1; ph::function<get_impl<int,int> > const get_int = get_impl<int, int>(); ph::function<get_impl<int&,int> > const get_int_ref = get_impl<int&,int>(); rule1 = qi::eps [ std::cout << ph::val("variant=") << qi::_r1 << ph::val("\n") ] >> qi::eps [ std::cout << ph::val("before=") << get_int(qi::_r1) << ph::val("\n") ] >> qi::eps [ get_int_ref(qi::_r1) = ph::val(7) ] >> qi::eps [ std::cout << ph::val("after=") << get_int(qi::_r1) << ph::val("\n") ] ; VariantType val(2134); TestSimpleRuleValidity("x",rule1( ph::ref(val) ), true); std::cout << val << "\n"; 

And everything seems to be working fine. Thank you again for your initial answer, which helps me a lot.

+1
source

Source: https://habr.com/ru/post/1340772/


All Articles