this article (boost spirit semantic action parameters) explains how to invalidate a match from a plain function with the signature
void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag)
I would like to invalidate a match from a member function of the grammar:
#include <boost/spirit/home/qi.hpp>
#include <boost/spirit/home/phoenix.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
class moduleAccessManager
{
public:
bool getModule(const std::string name)
{
if(name == "cat" || name == "dog")
return true;
else
return false;
}
};
void globalIsModule(std::string moduleName, const boost::spirit::unused_type&, bool& mFlag)
{
moduleAccessManager acm; /* Dirty workaround for this example */
if(acm.getModule(moduleName))
std::cout << "[isModule] Info: Found module with name >" << moduleName << "<" << std::endl;
else
{
std::cout << "[isModule] Error: No module with name >" << moduleName << "<" << std::endl;
mFlag = false; // No valid module name
}
}
template <typename Iterator, typename Skipper>
class moduleCommandParser : public qi::grammar<Iterator, Skipper>
{
private:
moduleAccessManager* m_acm;
qi::rule<Iterator, Skipper> start, module;
public:
std::string m_moduleName;
moduleCommandParser(moduleAccessManager* acm)
: moduleCommandParser::base_type(start)
, m_acm(acm)
, m_moduleName("<empty>")
{
module = qi::as_string[qi::lexeme[+(~qi::char_(' '))]]
[&globalIsModule] // This works fine
// [phoenix::bind(&moduleCommandParser::isModule, this)] // Compile error
;
start = module >> qi::as_string[+(~qi::char_('\n'))];
};
void isModule(std::string moduleName, const boost::spirit::unused_type&, bool& mFlag)
{
// Check if a module with moduleName exists
if(m_acm->getModule(moduleName))
std::cout << "[isModule] Info: Found module with name >" << moduleName << "<" << std::endl;
else
{
std::cout << "[isModule] Error: No module with name >" << moduleName << "<" << std::endl;
mFlag = false; // No valid module name
}
};
};
int main()
{
moduleAccessManager acm;
moduleCommandParser<std::string::const_iterator, qi::space_type> commandGrammar(&acm);
std::string str;
std::string::const_iterator first;
std::string::const_iterator last;
str = "cat run";
first = str.begin();
last = str.end();
qi::phrase_parse(first, last, commandGrammar, qi::space);
str = "bird fly";
first = str.begin();
last = str.end();
qi::phrase_parse(first, last, commandGrammar, qi::space);
}
Code on Coliru: http://coliru.stacked-crooked.com/a/4319b38a6d36c362
The important part is these two lines:
[&globalIsModule] // This works fine
// [phoenix::bind(&moduleCommandParser::isModule, this)] // Compile error
Using the global function works fine, but that's not an option for me because I need access to the m_acm object which is specific to the parser.
How can I bind a member function to a semantic action and at the same time be able to invalidate the match from that member function (using the 3 argument function signature mentioned above)?
There are two ways:
qi::_val
using Phoenix actorsbool&
) inside a "raw" semantic action functionAn example is here:
The anatomy of a semantic action function (with the third argument):
In your case you have a member function with roughly the "raw semantic action function" signature. Of course, you'll have to bind for the this
parameter (because it's a non-static member function).
Note that in this particular case, phoenix::bind
is not the right bind to use, as Phoenix Actors will be considered to be "cooked" (not raw) semantic actions, and they will get executed in the Spirit context.
You could either
use boost::bind
(or even std::bind
) to bind into a function that preserves the arity (!) of the member function:
[boost::bind(&moduleCommandParser::isModule, this, ::_1, ::_2, ::_3)]
This works: Live On Coliru
instead use a "cooked" semantic action, directly assigning to the _pass
context placeholder:
[qi::_pass = phoenix::bind(&moduleAccessManager::getModule, m_acm, qi::_1)]
This works too: Live On Coliru
The latter example, for future reference:
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
class moduleAccessManager {
public:
bool getModule(const std::string name) {
return name == "cat" || name == "dog";
}
};
void globalIsModule(std::string moduleName, const boost::spirit::unused_type&, bool& mFlag)
{
moduleAccessManager acm; /* Dirty workaround for this example */
if(acm.getModule(moduleName))
std::cout << "[isModule] Info: Found module with name >" << moduleName << "<" << std::endl;
else
{
std::cout << "[isModule] Error: No module with name >" << moduleName << "<" << std::endl;
mFlag = false; // No valid module name
}
}
template <typename Iterator, typename Skipper>
class moduleCommandParser : public qi::grammar<Iterator, Skipper>
{
private:
moduleAccessManager* m_acm;
qi::rule<Iterator, Skipper> start, module;
public:
std::string m_moduleName;
moduleCommandParser(moduleAccessManager* acm)
: moduleCommandParser::base_type(start)
, m_acm(acm)
, m_moduleName("<empty>")
{
using namespace phoenix::arg_names;
module = qi::as_string[qi::lexeme[+(~qi::char_(' '))]]
[qi::_pass = phoenix::bind(&moduleAccessManager::getModule, m_acm, qi::_1)]
;
start = module >> qi::as_string[+(~qi::char_('\n'))];
};
};
int main()
{
moduleAccessManager acm;
moduleCommandParser<std::string::const_iterator, qi::space_type> commandGrammar(&acm);
std::string str;
std::string::const_iterator first;
std::string::const_iterator last;
str = "cat run";
first = str.begin();
last = str.end();
std::cout << str << std::boolalpha
<< qi::phrase_parse(first, last, commandGrammar, qi::space)
<< "\n";
str = "bird fly";
first = str.begin();
last = str.end();
std::cout << str << std::boolalpha
<< qi::phrase_parse(first, last, commandGrammar, qi::space)
<< "\n";
}