1

I'm building a parser to execute commands that a user may enter on the command line. The first part of the command is the module it belongs to, the second part is the module's function to call.

Attached to the first parser is a semantic action (with boost::phoenix::ref()) that is supposed to store the name of the module in a variable m_moduleName. Attached to the 2nd parser is another semantic action that calls the function printParameters with the former variable as a parameter.

#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/home/qi.hpp>
#include <boost/bind.hpp>

#include <iostream>
#include <string>

namespace qi  = boost::spirit::qi;
namespace phoenix = boost::phoenix;


void printParameters(const std::string & module, const std::string & command)
{
    std::cout << "Module name during parse: " << module << std::endl;
    std::cout << "Command name during parse: " << command << std::endl;
}


template <typename Iterator>
struct myCommandParser : public qi::grammar<Iterator>
{
    myCommandParser() : myCommandParser::base_type(start)
    {
        start = qi::as_string[+(~qi::char_(' '))][phoenix::ref(m_moduleName) = qi::_1]
        >> qi::as_string[+(~qi::char_('\n'))][boost::bind(&printParameters, m_moduleName, ::_1)];
    };

    qi::rule<Iterator> start;
    std::string m_moduleName;
};


int main()
{
    myCommandParser<std::string::const_iterator> commandGrammar;

    commandGrammar.m_moduleName = std::string("initial_default");
    std::cout << "Module name before parsing: " << commandGrammar.m_moduleName << std::endl;

    std::string str("mod01 cmd02\n");
    std::string::const_iterator first = str.begin();
    std::string::const_iterator last = str.end();
    qi::parse(first, last, commandGrammar);

    std::cout << "Module name after parsing: " << commandGrammar.m_moduleName << std::endl;
}

Expected result: During the first semantic action the value of m_moduleName should be set to mod01 which should be printed during the printParameters function.

Actual result (program output):

Module name before parsing: initial_default
Module name during parse: 
Command name during parse:  cmd02
Module name after parsing: mod01

While constructing this minimal example I noticed that the value of m_moduleName is empty during the execution of the parse function, although it has been set to "initial_default" beforehand.

Can somebody please explain what exactly is going on here?

Why is the value empty instead of being mod01?

ChristophK
  • 733
  • 7
  • 20
  • Even though the syntax is so strange and magical, you need to keep in mind that the assignment of the `start` rule occurs inside `myCommandParser` constructor. [Example](http://coliru.stacked-crooked.com/a/d2de0d37cbc0b1d6). In the grammar constructor `boost::bind` generates an object similar to `[m_moduleName/*by current value*](const std::string& command){ printParameters(m_moduleName, command); }`. Using `boost::bind(&printParameters, boost::cref(m_moduleName), ::_1)` [seems to work](http://coliru.stacked-crooked.com/a/d81a32c98f40dd7c). – llonesmiz Nov 14 '15 at 14:45
  • But `phx::bind(&printParameters, phx::cref(m_moduleName), qi::_1)` as sehe recommends is probably a better alternative(for example if you need to pass two parameters instead of one). [Example](http://coliru.stacked-crooked.com/a/48509abadc1255d9). – llonesmiz Nov 14 '15 at 14:51

1 Answers1

1

You cannot mix Boost Bind/Phoenix/Lambda/Qi/std placeholders.

In fact you cannot use Boost Bind inside a semantic action.

You want to use use phoenix::bind with qi::_1. Aoh, and add phoenix::cref() around m_moduleName.

Other than that, you don't want to use ugly semantic actions in this case at all:

Simplified:

Live On Coliru

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

namespace qi = boost::spirit::qi;

int main()
{
    std::string const str("mod01 cmd02\n");
    std::string moduleName, command;
    qi::parse(str.begin(), str.end(), +~qi::char_(' ') >> +~qi::char_('\n'), moduleName, command);  

    std::cout << "Module name after parsing: " << moduleName << "\n";
    std::cout << "Command after parsing:     " << command    << "\n";
}

Prints

Module name after parsing: mod01
Command after parsing:      cmd02

See BOOST_FUSION_ADAPT_STRUCT and boost/fusion/adapted/std_pair.hpp e.g. for ways to scale/automate

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • The documentation of boost::spirit explicitly states that using boost::bind inside semantic actions is valid: http://www.boost.org/doc/libs/1_58_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html – ChristophK Jul 15 '15 at 08:21
  • I see that this answer might be helpful in some way, but my actual question was to explain what's going on here, not to give a solution for what you think might be what I want. I do want to use semantic actions, even though somebody might think they're evil. – ChristophK Jul 15 '15 at 08:26
  • Of course you can use boost bind to bind a raw semantic action but that's not what you want/need (it can't work like this). Also if you read carefully you'll note I never said semantic actions are evil – sehe Jul 15 '15 at 08:43
  • Perhaps you can yell us what you want to achieve instead. I can yell toy all about semantic actions. But I'll need to know what you need to do first – sehe Jul 15 '15 at 08:47
  • 1
    Note that in the forest paragraph o describe exactly what you need to do to fix the semantic action as is. Believe me I tested that before I posted it. – sehe Jul 15 '15 at 08:49
  • *yell* ;-) I would like to understand what's actually going on here. I'm relatively new to Boost::Spirit and all these overridden operators, template expression, etc. make me feel like I have no idea what's going on here. My primary goal is to understand, to learn, not to solve the problem in the most elegant way possible. – ChristophK Jul 15 '15 at 09:00
  • Well you have to understand that a semantic action cab either be a raw functor (with very intricate signatures passing the impregnation details of spirit parser components) or a Phoenix _lazy actor_. I think both are described on that page you linked. Your confusion stems from that, because boost bind does not result in a lazy actor (which your usage would suggest) and you weren't aware of that specific ddifference (?). – sehe Jul 15 '15 at 09:13
  • I'm not at a computer right now. If you want I can post a few variations later to contrast them. I think I've already done this once or twice in answers on SO. Will post a link if I find it quickly – sehe Jul 15 '15 at 09:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83300/discussion-between-christophk-and-sehe). – ChristophK Jul 15 '15 at 09:26