Frank Puck
Frank Puck

Reputation: 539

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;
}

Upvotes: 1

Views: 86

Answers (2)

Frank Puck
Frank Puck

Reputation: 539

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

Upvotes: 1

sehe
sehe

Reputation: 392911

Phoenix expressions combine phoenix actors. It's less typical to use them with raw function pointers (which your stateless lambda will decay to when applying operator+).

However, it should still be supported.

Without your real code, we can't but imagine some, so here goes:

Live On Compiler Explorer

#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string_view>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using It = std::string_view::const_iterator;

struct X : qi::grammar<It, double()> {
    X() : X::base_type(start) {
        using namespace qi::labels;

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

  private:
    struct factory {
        double value(double d) const { return d*d; }
    };
    std::unique_ptr<factory> m_sFactory{new factory()};

    qi::rule<It, double()> start;
};

int main() {
    X const x;

    for (std::string_view sv : {"0", "1", "2", "3.162278"}) {

        if (double v; parse(sv.begin(), sv.end(), x, v))
            std::cout << quoted(sv) << " -> " << v << std::endl;
        else
            std::cout << quoted(sv) << " -> FAILED" << std::endl;
    }
}

Printing

"0" -> 0
"1" -> 1
"2" -> 4
"3.162278" -> 10

With Capture

Same story:

Live On Coliru

#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string_view>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using It = std::string_view::const_iterator;

struct X : qi::grammar<It, double()> {
    X() : X::base_type(start) {
        using namespace qi::labels;

        start = qi::double_[ //
            _val = px::bind(
                [state = 2.0](factory const& _r, double const _d) { //
                    return state * _r.value(_d);
                },
                *m_sFactory, /*qi::*/ _1)];
    }

private:
    struct factory {
        double value(double d) const { return d*d; }
    };
    std::unique_ptr<factory> m_sFactory{new factory()};

    qi::rule<It, double()> start;
};

int main() {
    X const x;

    for (std::string_view sv : {"0", "1", "2", "3.162278"}) {

        if (double v; parse(sv.begin(), sv.end(), x, v))
            std::cout << quoted(sv) << " -> " << v << std::endl;
        else
            std::cout << quoted(sv) << " -> FAILED" << std::endl;
    }
}

Prints

"0" -> 0
"1" -> 2
"2" -> 8
"3.162278" -> 20

Upvotes: 1

Related Questions