Reputation: 53
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
Upvotes: 4
Views: 1567
Reputation: 53
I think I found a way how to do it. (Don't know if this is best way, but it works ;-) ). The problem was in int& type, because boost::variant holds int and not int&. So I update your template to accept two types, one for variant getter and one for return type.
I updated get_impl template in this way:
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 works Ok. Thank you again hkaiser for your initial reply which helps me a lot.
Upvotes: 1
Reputation: 11521
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) ]
Upvotes: 3