3

I am new to boost spirit and I have the following problem:

#include <string>
#include <vector>
#include <boost/spirit/include/qi.hpp>

#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

#include <boost/bind.hpp>

using namespace boost::spirit;
using namespace std;

struct MyGrammar
    : qi::grammar<string::const_iterator, string(), ascii::space_type> {
    MyGrammar();

    void myFun(const string& s);

    private:
    qi::rule<string::const_iterator, string(), ascii::space_type> myRule;
};



using namespace boost::spirit;
using namespace std;

MyGrammar::MyGrammar() : MyGrammar::base_type(myRule) {
    using qi::_1;

    myRule = int_ [boost::bind(&MyGrammar::myFun, this, _1)]; // fails
    myRule = int_ [_val = _1];  // fine
}

void MyGrammar::myFun(const string& s){
    cout << "read: " << s << endl;
}

int
main(){
}

With the first assignment of myRule I get compile errors while the second assignment compiles fine.

In the first case the compiler outputs huge error messages that I don't understand. At the end it says:

boost_1_49_0/include/boost/bind/bind.hpp:318:9: error: no match for call to '(const boost::_mfi::mf1<void, MyGrammar, const std::basic_string<char>&>) (MyGrammar* const&, const boost::phoenix::actor<boost::spirit::argument<0> >&)'
boost_1_49_0/include/boost/bind/mem_fn_template.hpp:163:7: note: candidates are: R boost::_mfi::mf1<R, T, A1>::operator()(T*, A1) const [with R = void, T = MyGrammar, A1 = const std::basic_string<char>&]
boost_1_49_0/include/boost/bind/mem_fn_template.hpp:184:7: note:                 R boost::_mfi::mf1<R, T, A1>::operator()(T&, A1) const [with R = void, T = MyGrammar, A1 = const std::basic_string<char>&]

Any ideas? Thanks a lot for any help!

ildjarn
  • 62,044
  • 9
  • 127
  • 211
frank
  • 481
  • 8
  • 16

2 Answers2

5

You can't use placeholders from different bind implementations. There are currently three bind functions in Boost:

  • boost::bind, superseded by
  • boost::lambda::bind, superseded by
  • boost::phoenix::bind, which is what you should use for Boost.Spirit

The placeholders under boost::spirit::qi (and boost::spirit::karma) are the same as the ones used by boost::phoenix::bind, so just use that.

Oh, and pro tip: Stop your using namespace std; and preferrably any other using directive in global namespace.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • +1 - I'm not too sure whether `qi::_1` will always alias `phoenix::qi`, really. I'd rather suggest using `qi::_1` (or in fact, locally `using namespace qi`). After all, you're using the Qi library and there is no need to use inside information. – sehe May 25 '12 at 21:15
3

The first problem is that you specifiy std::string as your synthesized attribute, but then define your rule in terms of qi::int_, which has a synthesized attribute of int.

The second problem is that, as the Spirit docs state directly, non-Phoenix functors take three arguments, not one:

You can use Boost.Bind to bind member functions. For function objects, the allowed signatures are:

void operator()(Attrib const&, unused_type, unused_type) const;
void operator()(Attrib const&, Context&, unused_type) const;
void operator()(Attrib const&, Context&, bool&) const;

The third problem is that you're using Spirit's Phoenix _1 placeholder rather than boost::bind's placeholder (which is effectively in the global namespace).

In summary, this should work:

#include <string>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

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

struct MyGrammar :
    qi::grammar<std::string::const_iterator, int(), ascii::space_type>
{
    MyGrammar();
    void myFun(int i, qi::unused_type, qi::unused_type);

private:
    qi::rule<std::string::const_iterator, int(), ascii::space_type> myRule;
};

MyGrammar::MyGrammar() : MyGrammar::base_type(myRule)
{
    myRule = qi::int_[boost::bind(&MyGrammar::myFun, this, _1, _2, _3)];
}

void MyGrammar::myFun(int const i, qi::unused_type, qi::unused_type)
{
    std::cout << "read: " << i << '\n';
}

int main()
{
    std::string const input = "42";
    std::string::const_iterator first = input.begin(), last = input.end();
    qi::phrase_parse(first, last, MyGrammar(), ascii::space);
}

That being said, unless you have a very specific reason to use boost::bind here, you should be using boost::phoenix::bind instead:

#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>

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

struct MyGrammar :
    qi::grammar<std::string::const_iterator, int(), ascii::space_type>
{
    MyGrammar();
    void myFun(int i);

private:
    qi::rule<std::string::const_iterator, int(), ascii::space_type> myRule;
};

MyGrammar::MyGrammar() : MyGrammar::base_type(myRule)
{
    myRule = qi::int_[boost::phoenix::bind(&MyGrammar::myFun, this, qi::_1)];
}

void MyGrammar::myFun(int const i)
{
    std::cout << "read: " << i << '\n';
}

int main()
{
    std::string const input = "42";
    std::string::const_iterator first = input.begin(), last = input.end();
    qi::phrase_parse(first, last, MyGrammar(), ascii::space);
}

This allows your bound member function to take only a single argument – the synthesized attribute – as you originally wanted.

ildjarn
  • 62,044
  • 9
  • 127
  • 211