c++c++11boost-spiritboost-phoenix

boost::spirit and lambda functions


My boost spirit code using lambdas for production of AST elements, does only compile if I use lambdas without capture and WITH a leading +.

does not compile:

_val = boost::phoenix::bind(
    [&](const double _d)
    {    return m_sFactory->value(_d);
    },
    qi::_1
)

compiles fine:

_val = boost::phoenix::bind(
    +[](const factory&_r, const double _d)
    {    return _r.value(_d);
    },
    *m_sFactory,
    qi::_1
)

Why is that? Error is pretty much identical between compilers. It seems that phoenix only supports ordinary function pointers. This applies to boost 1.80 and many contemporary compilers:

user@txa-user-7560:/mnt/c/Users/user/Documents/hierarchy/spectre_parser/spectre_parser$ clang++-15 -std=c++11 spectre_parser.cpp -lpthread|& tee make.out
In file included from spectre_parser.cpp:6:
./spectre_parser.h:238:11: error: no matching function for call to 'bind'
                        _val = boost::phoenix::bind(
                               ^~~~~~~~~~~~~~~~~~~~
spectre_parser.cpp:14:54: note: in instantiation of member function 'afs::spectre_parser::name_value_pairs<const char *>::name_value_pairs' requested here
                afs::spectre_parser::name_value_pairs<const char*> sGrammar(sFactory);
                                                                   ^
/usr/local/include/boost/phoenix/bind/bind_function.hpp:58:5: note: candidate template ignored: could not match 'RT (*)(T...)' against '(lambda at ./spectre_parser.h:240:5)'
    bind(RT (*f)(T...), A const&... a)
    ^
1 error generated.
user@txa-user-7560:/mnt/c/Users/user/Documents/hierarchy/spectre_parser/spectre_parser$

C++ code to demonstrate the problem (just remove the + in front of one of the lambda functions):

#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/bind/bind_function.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/flyweight.hpp>
#include <boost/flyweight/set_factory.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <utility>

namespace parser
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

template<typename T>
struct compare;
typedef std::pair<std::string, bool> stringAndBool;
typedef boost::flyweights::flyweight<stringAndBool, boost::flyweights::set_factory<compare<stringAndBool> > > STRINGFW;
template<>
struct compare<stringAndBool>
{   bool operator()(const stringAndBool&_r0, const stringAndBool&_r1) const
    {   return _r0 < _r1;
    }
};
typedef std::vector<STRINGFW> vectorOfStringAndBool;
template<>
struct compare<vectorOfStringAndBool>
{   bool operator()(const vectorOfStringAndBool&_r0, const vectorOfStringAndBool&_r1) const
    {   return _r0 < _r1;
    }
};
typedef boost::flyweights::flyweight<vectorOfStringAndBool, boost::flyweights::set_factory<compare<vectorOfStringAndBool> > > vectorOfStringAndBoolFW;


template<typename IT>
struct skipper:qi::grammar<IT>
{   skipper(void)
        :skipper::base_type(start)
    {
    }
    qi::rule<IT> start = (
        ("\\" >> qi::eol)
        | +(qi::blank - qi::eol)
        | ("//" >> *(qi::char_ - qi::eol))
        | (qi::eol >> '+')
        | (qi::eol >> '*' >> *(qi::char_ - qi::eol) >> qi::eol)
        | (qi::eol >> *(qi::space - qi::eol) >> qi::eol)
    );
};
template<typename IT>
struct node:qi::grammar<IT, STRINGFW(), skipper<IT> >
{   qi::rule<IT, STRINGFW(), skipper<IT> > m_sStart;
    node(void)
        :node::base_type(m_sStart, "node")
    {   m_sStart = qi::lexeme[+(ascii::alnum | qi::char_('_'))][
            qi::_val = boost::phoenix::bind(
                +[](const std::vector<char>&_r)
                {   return STRINGFW(std::make_pair(std::string(_r.begin(), _r.end()), true));
                },
                qi::_1
            )
        ];
        m_sStart.name("node");
        BOOST_SPIRIT_DEBUG_NODE(m_sStart);
    }
};
template<typename IT>
struct nodes_grammar:qi::grammar<IT, vectorOfStringAndBoolFW(), skipper<IT> >
{   node<IT> m_sNode;
    qi::rule<IT, vectorOfStringAndBoolFW(), skipper<IT> > m_sNodes;

    nodes_grammar(void)
        :nodes_grammar::base_type(m_sNodes, "nodes")
    {   //BOOST_SPIRIT_DEBUG_NODE(m_sNode);
        m_sNodes = ('(' > (+m_sNode) > ')')[
            qi::_val = boost::phoenix::bind(
                +[](const std::vector<STRINGFW>&_r)
                {   return vectorOfStringAndBoolFW(_r);
                },
                qi::_1
            )
        ] | (+m_sNode)[
            qi::_val = boost::phoenix::bind(
                +[](const std::vector<STRINGFW>&_r)
                {   return vectorOfStringAndBoolFW(_r);
                },
                qi::_1
            )
        ];
        m_sNodes.name("one or more nodes");
        BOOST_SPIRIT_DEBUG_NODE(m_sNodes);
    }
};

}
int main()
{   parser::nodes_grammar<const char*> sGrammar;
    static constexpr char ac[] = "(a 0 b1 0 1 0)";
    parser::vectorOfStringAndBoolFW sV;
    const char *p = ac;
    if (!boost::spirit::qi::phrase_parse(p, ac + sizeof ac - 1, sGrammar, parser::skipper<const char*>(), sV) || p != ac + sizeof ac - 1)
        std::cerr << "error" << std::endl;
    else
        std::cerr << "success" << std::endl;
}

Solution

  • boost::phoenix::bind() seems to have different versions.

    In order to fix this problem, one needs to do

    #include <boost/phoenix/bind/bind_function_object.hpp>
    

    and suddenly lambda functions can be used.

    See also

    Binding Function Objects