c++ - Is it possible to attach an action to a boost::spirit::rule parser which assigns the parsed result to a member of a (yet) unknown instance? -
i'm trying reference member of (yet) unknown instance within boost::spirit rule definitions' action, in pseudocode,
instead of double_[ref(rn) = _1] i'm looking x** ppx; double_[ref(&x::rn, ppx) = _1]
a workaround simple "semantic action" parameter knows instance , able write it, like
qi::rule<iterator, skipper> start; my_grammar(datacontext*& datacontext) : my_grammar::base_type(start) , execcontext(execcontext) { start = qi::double_[ boost::bind(&my_grammar::newvalueforxy, datacontext, ::_1) ];
however, i'm wondering if there possibility "bind" directly member variable it's possible bind "local" variable using "phoenix::ref(...) = value".
i tried following syntax:
start = qi::int_[ boost::bind<int&>(&datacontext::newvalueforxy, boost::ref(datacontext))() = ::_1] ];
but failed vs2010sp1 , error message
error c2440: '=': 'boost::arg' cannot converted ...
there several ways skin cat:
- you thought of deferring execution of bind expression:
phx::bind
can that - alternatively, use attribute propagation (and without semantic actions altogether)
- lastly, use inherited attributes (e.g. when datacontext has no default constructor or copying expensive)
1. deferring bind phoenix
use phoenix bind purpose: result in phoenix actor, "deferred executed" @ time semantic action triggered.
here's reconstruction of missing code sample might have been after:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; namespace phx= boost::phoenix; struct datacontext { double xy; }; template <typename iterator, typename skipper> struct my_grammar : qi::grammar<iterator, skipper> { my_grammar(datacontext& datacontext) : my_grammar::base_type(start) { start = qi::double_ [ phx::bind(&my_grammar::newvalueforxy, phx::ref(datacontext), qi::_1) ]; } private: static void newvalueforxy(datacontext& dc, double value) { dc.xy = value; } qi::rule<iterator, skipper> start; }; int main() { const std::string s = "3.14"; datacontext ctx; my_grammar<decltype(begin(s)), qi::space_type> p(ctx); auto f(begin(s)), l(end(s)); if (qi::phrase_parse(f, l, p, qi::space)) std::cout << "success: " << ctx.xy << "\n"; }
note:
- phx::ref() wrap reference datacontext
- qi::_1 instead of boost::_1 placeholder
given implementation of
newvalueforxy
have writtenstart = qi::double_ [ phx::bind(&datacontext::xy, phx::ref(datacontext)) = qi::_1 ];
2. use attribute grammar, instead of semantic actions
however, i'd write same example using attributes instead of semantic actions (because that's they for):
#include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; namespace phx= boost::phoenix; struct datacontext { double xy; }; boost_fusion_adapt_struct(datacontext, (double, xy)) template <typename iterator, typename skipper> struct my_grammar : qi::grammar<iterator, datacontext(), skipper> { my_grammar() : my_grammar::base_type(start) { start = qi::double_; } private: qi::rule<iterator, datacontext(), skipper> start; }; int main() { const std::string s = "3.14"; static const my_grammar<decltype(begin(s)), qi::space_type> p; datacontext ctx; if (qi::phrase_parse(begin(s), end(s), p, qi::space, ctx)) std::cout << "success: " << ctx.xy << "\n"; }
3. use inherited attributes pass in context reference
if absolutely insist, can use inherited attributes
purpose:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; namespace phx= boost::phoenix; struct datacontext { double xy; }; template <typename iterator, typename skipper> struct my_grammar : qi::grammar<iterator, void(datacontext&), skipper> { my_grammar() : my_grammar::base_type(start) { start = qi::double_ [ phx::bind(&datacontext::xy, qi::_r1) = qi::_1 ]; } qi::rule<iterator, void(datacontext&), skipper> start; }; int main() { const std::string s = "3.14"; const static my_grammar<std::string::const_iterator, qi::space_type> p; datacontext ctx; if(qi::phrase_parse(begin(s), end(s), p(phx::ref(ctx)), qi::space)) { std::cout << "success: " << ctx.xy << "\n"; } }
this more expressive @ call site:
qi::phrase_parse(begin(s), end(s), p(phx::ref(ctx)), qi::space));
and doesn't require context default constructible.
Comments
Post a Comment