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