Search code examples
c++boostboost-spiritboost-fusion

How to bind/call boost::function stored in fusion::vector from spirit semantic rule?


I am trying to map some keyword handlers (methods) via qi::symbols with values of boost::function type. So If keyword is found I want to call method. But I couldn't bind method from this map. Compiler failed with bunch of errors on phoenix::bind. What do I do wrong?

Part of code is below:

template <typename Iterator>
struct Grammar : qi::grammar<Iterator, AST::FunctionCall(), ascii::space_type>
{
    Grammar():
        Grammar::base_type(query),
    {
        ...
        operand =
          predicate [phoenix::bind(phoenix::at_c<0>(qi::_1), this, phoenix::at_c<1>(qi::_1))]; // **Compiler fails here**
        ...

        predicate = 
            (pred_tbl > '(')
         > -(primary_expr % ',')
         > ')';
        ... 

        pred_tbl.add
            ("composing",    &RQL::composing)
        );
    }

    qi::rule<Iterator, fusion::vector<Predicate, PredicateArgList>(), ascii::space_type>   predicate;

    typedef std::vector<AST::Value> PredicateArgList;
    typedef boost::function<void (Grammar*, const PredicateArgList& args)> Predicate;   
    qi::symbols<char, Predicate> pred_tbl;

    void composing(const PredicateArgList& args);
};

Compiler errors:

error C2903: 'result' : symbol is neither a class template nor a function template  c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  115 
error C2039: 'result' : is not a member of 'boost::function<Signature>' c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  115 
error C2059: syntax error : '<' c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  115 
error C2238: unexpected token(s) preceding ';'  c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  116 
error C2065: 'function_apply' : undeclared identifier   c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  124 
error C2955: 'boost::mpl::eval_if' : use of class template requires template argument list  c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  125 
error C2146: syntax error : missing ';' before identifier 'type'    c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 
error C3254: 'boost::phoenix::detail::function_eval<2>::result<Env,F,A0,A1>' : class contains explicit override 'type' but does not derive from an interface that contains the function declaration c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 
error C2838: 'type' : illegal qualified name in member declaration  c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int   c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 
error C2602: 'boost::phoenix::detail::function_eval<2>::result<Env,F,A0,A1>::type' is not a member of a base class of 'boost::phoenix::detail::function_eval<2>::result<Env,F,A0,A1>'   c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 
error C2868: 'boost::phoenix::detail::function_eval<2>::result<Env,F,A0,A1>::type' : illegal syntax for using-declaration; expected qualified-name  c:\work\include\boost-1_41\boost\spirit\home\phoenix\core\detail\function_eval.hpp  126 

Solution

  • I suppose the missing link might have been

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    

    See it live on http://liveworkspace.org/code/3uvyb4$1

    It prints

    Hello world from 'Grammar::composing(PredicateArgList const&)' args:3
    

    as expected.

    UPDATE PS. I'd probably prefer writing the bind invocation like so:

    Grammar(): Grammar::base_type(predicate)
    {
        using phx::bind;
        using namespace qi;
    
        as<PredicateArgList> coerce;
    
        predicate = 
            (pred_tbl > '('
                      > coerce [ -(primary_expr % ',') ]
                      > ')') 
            [ phx::bind(_1, this, _2)]
            ;
    
        // ...
    

    I think it is much clearer with _1 and _2 instead of the at_c<n> invocations.

    Here is the code for reference:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/function.hpp>
    #include <boost/phoenix/fusion.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    namespace qi     = boost::spirit::qi;
    namespace fusion = boost::fusion;
    namespace ascii  = boost::spirit::ascii;
    namespace phx    = boost::phoenix;
    
    namespace AST
    {
        struct FunctionCall {};
        using Value = int;
    }
    
    template <typename Iterator>
    struct Grammar : qi::grammar<Iterator, AST::FunctionCall(), ascii::space_type>
    {
        Grammar(): Grammar::base_type(predicate)
        {
            using phx::bind;
            using namespace qi;
    
            as<PredicateArgList> coerce;
    
            predicate = 
                (pred_tbl > '('
                          > coerce [ -(primary_expr % ',') ]
                          > ')') 
                [ phx::bind(_1, this, _2)]
                ;
    
            pred_tbl.add
                ("composing",    &Grammar::composing)
            ;
    
            primary_expr = qi::int_;
        }
    
        typedef std::vector<AST::Value> PredicateArgList;
        typedef boost::function<void (Grammar*, PredicateArgList const&)> Predicate;   
        qi::symbols<char, Predicate> pred_tbl;
    
        qi::rule<Iterator, AST::Value(), ascii::space_type>   primary_expr;
        qi::rule<Iterator, AST::FunctionCall(), ascii::space_type>   predicate;
    
        void composing(const PredicateArgList& args) 
        {
            std::cout << "Hello world from 'Grammar::composing(PredicateArgList const&)' args:" << args.size() << "\n";
        }
    };
    
    int main()
    {
        const std::string input("composing (1, 2, 3)");
        auto f = begin(input), l = end(input);
    
        Grammar<decltype(f)> p;
    
        bool ok = qi::phrase_parse(f,l,p,ascii::space);
        return ok?0:255;
    }