c++ - cannot construct std::string from placeholder in Boost.Spirit -
i have started working on boost.spirit based simple parser parse c++-like file (the c++-ish part built-in template types; e.g. map<string, smart_ptr<int>> name_object_map;
- built-in compiler, , user cannot declare template classes). nevertheless, grammar intended contain data structure declarations, , not expressions, except constant expressions used initialization of enumerator declarations; enum e { = 4 * 5 + 3 };
valid. not problem me, because couldn't parse e
way want yet :)
i have made following yesterday, after reading docs , examples, doesn't compile:
#include <boost/spirit/include/phoenix.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi_char_class.hpp> #include <cassert> #include <memory> #include <string> #include <utility> struct context {}; class foo { std::string name; const context *ctx; public: foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} }; using foo_ref = std::shared_ptr<foo>; template <typename iterator> struct skipper : boost::spirit::qi::grammar<iterator> { skipper() : skipper::base_type(start) { using namespace boost::spirit; qi::char_type char_; ascii::space_type space; start = space // tab/space/cr/lf | "/*" >> *(char_ - "*/") >> "*/" // c-style comments ; } boost::spirit::qi::rule<iterator> start; }; template <typename iterator> struct the_parser : boost::spirit::qi::grammar<iterator, std::vector<foo_ref>(), skipper<iterator>> { the_parser() : the_parser::base_type(start), current_context(&root) { using namespace boost::spirit; namespace phx = boost::phoenix; identifier = qi::lexeme[qi::alpha >> *qi::alnum]; start = *(foo_decl); // currently, no semantic action attached. // create root decl in ast. foo_decl = (lit("foo") >> identifier)[qi::_val = std::make_shared<foo>( qi::_1, current_context)] >> qi::char_('{') >> qi::char_('}') >> qi::char_(';'); boost_spirit_debug_nodes((identifier)(start)(foo_decl)); } boost::spirit::qi::rule<iterator, std::string(), skipper<iterator>> identifier; boost::spirit::qi::rule<iterator, std::vector<foo_ref>(), skipper<iterator>> start; boost::spirit::qi::rule<iterator, foo_ref(), skipper<iterator>> foo_decl; context root; const context *current_context; }; int main() { the_parser<std::string::const_iterator> parser; std::vector<foo_ref> root; const std::string content = "foo john_doe { };"; auto first = content.cbegin(), last = content.cend(); bool r = boost::spirit::qi::phrase_parse( first, last, parser, skipper<std::string::const_iterator>(), root); assert(r && first == last); }
compiling clang on mac (the first line std::make_shared
):
error: no matching constructor initialization of 'foo' __second_(_vstd::forward<_args2>(_vstd::get<_i2>(__second_args))...) ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ... note: candidate constructor not viable: no known conversion 'const boost::phoenix::actor<boost::spirit::argument<0> >' 'const std::string' (aka 'const basic_string<char, char_traits<char>, allocator<char> >') 1st argument foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} ^
in foo_decl
s semantic action, cannot construct foo
(through std::make_shared
), because first attribute's result cannot converted std::string
. however, if add class member std::string s
, , instead, works:
foo_decl = (lit("foo") >> identifier)[boost::phoenix::ref(s) = qi::_1] >> qi::char_('{') >> qi::char_('}') >> qi::char_(';');
likewise, if try std::cout
it, can see john_doe
printed.
if bind member function call phoenix, works:
foo_decl = (lit("foo") >> identifier)[qi::_val = boost::phoenix::bind(&the_parser, this, qi::_1)] >> qi::char_('{') >> qi::char_('}') >> qi::char_(';'); foo_ref make_foo(const std::string &n) { return std::make_shared(n, current_context); }
these last 3 workarounds mean there implicit conversion sequence decltype(qi::_1)
std::string
; isn't correct?
i happy, if show me mistake or gap in understanding of how semantic actions, , placeholders work. looks strange me why std::make_shared
doesn't work.
thank you!
first of all:
i'd steer cleer smart pointers in spirit
i'd steer away semantic actions if don't need them (you don't) boost spirit: "semantic actions evil"?
the problem placeholder: can't use statically, need use in lazy expression (phoenix actor)
using phoenix::function first link:
#define boost_spirit_debug #include <boost/spirit/include/phoenix.hpp> #include <boost/spirit/include/qi.hpp> #include <cassert> #include <memory> #include <string> #include <utility> namespace { template <typename t> struct make_shared_f { template <typename... a> struct result { typedef std::shared_ptr<t> type; }; template <typename... a> typename result<a...>::type operator()(a &&... a) const { return std::make_shared<t>(std::forward<a>(a)...); } }; template <typename t> using make_shared_ = boost::phoenix::function<make_shared_f<t> >; } struct context {}; class foo { std::string name; const context *ctx; public: foo(const std::string &name, const context *ctx) : name(name), ctx(ctx) {} }; using foo_ref = std::shared_ptr<foo>; template <typename iterator> struct skipper : boost::spirit::qi::grammar<iterator> { skipper() : skipper::base_type(start) { using namespace boost::spirit; qi::char_type char_; ascii::space_type space; start = space // tab/space/cr/lf | "/*" >> *(char_ - "*/") >> "*/" // c-style comments ; } boost::spirit::qi::rule<iterator> start; }; template <typename iterator> struct the_parser : boost::spirit::qi::grammar<iterator, std::vector<foo_ref>(), skipper<iterator> > { the_parser() : the_parser::base_type(start), current_context(&root) { using namespace boost::spirit; namespace phx = boost::phoenix; identifier = qi::alpha >> *qi::alnum; // create root decl in ast. foo_decl = ("foo" >> identifier) [qi::_val = make_shared_<foo>{}(qi::_1, current_context)] >> '{' >> '}' >> ';'; start = *foo_decl; // currently, no semantic action attached. boost_spirit_debug_nodes((identifier)(start)(foo_decl)); } boost::spirit::qi::rule<iterator, std::string()> identifier; boost::spirit::qi::rule<iterator, foo_ref(), skipper<iterator> > foo_decl; boost::spirit::qi::rule<iterator, std::vector<foo_ref>(), skipper<iterator> > start; context root; const context *current_context; }; int main() { the_parser<std::string::const_iterator> parser; std::vector<foo_ref> root; const std::string content = "foo johndoe { };"; auto first = content.cbegin(), last = content.cend(); bool r = boost::spirit::qi::phrase_parse(first, last, parser, skipper<std::string::const_iterator>(), root); if (r) std::cout << "success\n"; else std::cout << "failed\n"; if (first != last) std::cout << "remaining unparsed: '" << std::string(first,last) << "'\n"; }
prints
success
along debug output of
<start> <try>foo johndoe { };</try> <foo_decl> <try>foo johndoe { };</try> <identifier> <try>johndoe { };</try> <success> { };</success> <attributes>[[j, o, h, n, d, o, e]]</attributes> </identifier> <success></success> <attributes>[0x60600000ebb0]</attributes> </foo_decl> <foo_decl> <try></try> <fail/> </foo_decl> <success></success> <attributes>[[0x60600000ebb0]]</attributes> </start>
Comments
Post a Comment