I’m using QI and Phoenix, and I want to write a small grammar that returns 4 bools which are to be used as arguments for a function call inside a semantic action.
I have several functions that need those things, and so far I have used this approach:
( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)
[px::bind(&Bool4Function, spirit::_val, spirit::_1, spirit::_2, spirit::_3, spirit::_4)]
and while it’s okay on it’s own, using it all over the place is just plain ugly and confusing, even with ‘using’ the namespace parts.
That’s why I wanted to extract this expression into a standalone grammar.
So I tried this (credit goes to ildjarn for the testbed):
///// grammar implementation /////
#include <boost/fusion/include/vector10.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_string.hpp>
struct FourBools : boost::spirit::qi::grammar<
char const*,
boost::fusion::vector4<bool, bool, bool, bool>()
>
{
typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type;
FourBools() : base_type(start_)
{
using boost::spirit::bool_;
start_
= "4bools:"
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ';'
;
}
private:
boost::spirit::qi::rule<
base_type::iterator_type,
base_type::sig_type
> start_;
};
FourBools const fourBools;
///// demonstration of use /////
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>
void noDice(bool a, bool b, bool c, bool d)
{
}
void worksFine(boost::fusion::vector4<bool, bool, bool, bool> a)
{
}
int main()
{
namespace phx = boost::phoenix;
namespace spirit = boost::spirit;
std::string const input("4bools:true,true,true,false;");
char const* first = input.c_str();
char const* const last = first + input.size();
bool const success = spirit::qi::parse(
first, last,
fourBools[phx::bind(&noDice, spirit::_1)]
);
if (!success)
std::cout << "parse() failed\n";
else if (first != last)
std::cout << "didn't consume all input\n";
std::cout.flush();
}
That doesn’t compile unless fourBools[phx::bind(&noDice, spirit::_1)] is replaced with fourBools[phx::bind(&worksFine, spirit::_1)].
That means, my problem is the unpacking of arguments to match the signature of the function to be called, since the number of arguments differ at signature level (one tuple of four bools, vs four bools on their own).
Is it possible to unpack using phoenix placeholders directly, instead of writing wrappers which translate tuples into individual arguments for my existing functions that need them separate?
If it is, what would be the syntax for that?
After all, an inline version like ( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool) works fine when ‘unpacked’ by spirit::_1 - spirit::_4, placeholders.
That makes it appear to me as if this version returns a tuple as well, and is somehow unpackable with the above approach, unlike a grammar that returns one.
How do I deal with this?
It’s pretty much impossible to diagnose your issue if you don’t post a complete, coherent repro; it could be a syntax error, it could be a missing
#include, who knows..?Here’s a working demonstration; hopefully you can use it as a reference to figure out what’s wrong with your code:
As an aside, I think using a tuple with purely homogeneous types is strange; personally, I’d change the grammar’s synthesized attribute to
boost::array<bool, 4>.EDIT (in response to OP’s edit): There’s good news and bad news and more good news.
Here’s the good news: Boost.Fusion has functionality to do exactly what you want to do with minimal code:
boost::fusion::fused<>. This will take a callable type (including free-function pointers and member-function pointers) that takes multiple arguments and wrap that callable type in a functor that takes a Fusion sequence; when this functor is invoked, it takes the Fusion sequence and unpacks it, forwarding the individual elements of the tuple to the wrapped callable type as separate arguments.So, given the grammar I already posted and the following:
boost::spirit::qi::parse()can be called like so:Here’s the bad news: Boost.Fusion’s callable type wrappers rely on the TR1/C++11
result_ofprotocol, while Boost.Phoenix v2 implements the Boost.Lambdaresult_ofprotocol – these are not compatible. As a result, you must unpack the tuple elements yourself:Yuck! But, there’s more good news: Boost.Phoenix v3 is going to be released in Boost 1.47, and it implements the TR1/C++11
result_ofprotocol. Consequently, starting with Boost 1.47 you’ll be able to useboost::fusion::fused<>and save yourself some tedious boilerplate.