2

I'm trying to create a rule that returns a function<char(char const *)> constructed by currying a Phoenix expression. E.g.,

start = int_[_val = xxx];
rule<Iterator, function<char(char const *)> start;

What should xxx be so that parsing the string "5" should give me a function that gives me the fifth character of its input? I've tried things like lambda(_a = arg1)[arg1[_a]](_1) might work, but I've not been able to hit on the magic formula.

In other words, I'd like the attribute to curry arg2[arg1] on the value of the parsed int

Very grateful for any suggestions. Note that I'm on VC2008, so C++11 lambdas not available.

Mike

sehe
  • 374,641
  • 47
  • 450
  • 633
user2913094
  • 981
  • 9
  • 16

1 Answers1

2

After fixing that rule declaration:

typedef boost::function<char(char const*)> Func;
qi::rule<Iterator, Func()> start;

it worked: Live On Coliru (c++03).

UPDATE:

Why did I end up with such a complex contraption?

qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1)

Well. Let me tell you about the joy of complexing functional composition with lazy evaluation (in C++ template meta-programming that has these surprises with reference/value semantics): Don't do the following:

qi::_val = px::lambda(_a = qi::_1) [arg1[_a]] // UB!!! DON'T DO THIS

Depending on the compiler, optimization level, this might *appear to work. But it's invoking Undefined Behaviour [1]. The problem is that qi::_1 will be kept as a reference to the attribute exposed by qi::int_ parser expression. However, this reference, after the lifetime of the parser context has ended, is a dangling reference.

So evaluating the functor indirects through an invalid reference. To avoid this you should say (Live On Coliru):

qi::_val = px::lambda(_a = px::val(qi::_1)) [arg1[_a]]

or even (if you like obscure code):

qi::_val = px::lambda(_a = +qi::_1) [arg1[_a]]

Or, you know, you can stick with the bound nested lambda, since the bind defaults to value-semantics for qi::_1 (unless you used the phx::cref/phx::ref wrappers).

I hope the above analysis drives home the point I made in the comments earlier:

Note that I wouldn't recommend this code style. Higher-order programming with Phoenix is tricky enough without composing them from within lazy actors in some embedded expression-template DSL: qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1). 'Nuff said?


#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/function.hpp>

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

typedef boost::function<char(char const*)> Func;

int main()
{
    typedef std::string::const_iterator Iterator;
    using namespace boost::phoenix::arg_names;

    qi::rule<Iterator, Func()> start;

    start = qi::int_ 
            [ qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1) ];
    // or:  [ qi::_val = px::lambda(_a = px::val(qi::_1))[arg1[_a]] ];

    static char const* inputs[] = { "0", "1", "2", "3", "4", 0 };

    for (char const* const* it = inputs; *it; ++it)
    {
        std::string const input(*it);
        Iterator f(input.begin()), l(input.end());

        Func function;
        bool ok = qi::parse(f, l, start, function);

        if (ok)
            std::cout << "Parse resulted in function() -> character " 
               << function("Hello") << "; " 
               << function("World") << "\n";
        else
            std::cout << "Parse failed\n";

        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f, l) << "'\n";
    }
}

Prints

Parse resulted in function() -> character H; W
Parse resulted in function() -> character e; o
Parse resulted in function() -> character l; r
Parse resulted in function() -> character l; l
Parse resulted in function() -> character o; d

[1] (MSVC2013 appeared to crash, gcc may appear to work in -O3, but segfaults in -O0 etc.)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for responding, sehe, but unfortunately I don't think that resolves my question. Unless I'm missing something, the above code seems to ignore the integer that was parsed, which is what I was trying to incorporate. What I want is if the input is `"2"`, then `output("Hello")` should be `e`. Do you know how I can accomplish that? – user2913094 Sep 23 '14 at 09:01
  • @user2913094 Wokay. I updated the sample to be more complete. Note that I wouldn't recommend this code style. Higher-order programming with Phoenix is tricky enough without composing them from within lazy actors in some embedded expression-template DSL: `qi::_val = px::bind(px::lambda[arg1[arg2]], px::lambda[arg1], qi::_1)`. ***'Nuff said`***? – sehe Sep 23 '14 at 09:09
  • 1
    Seems like good advice. The alternatives (E.g., creating a functor class and avoiding a lambda altogether) are a little clumsy, but at least they're understandable. Thanks! – user2913094 Sep 23 '14 at 09:20
  • @user2913094 I've added an update with a more elegant, but **very subtly** tricky version. I also explain why it is so tricky. Conclusion: I don't recommend this for production code – sehe Sep 23 '14 at 11:39
  • I had to modify your more elegant solution to – user2913094 Sep 23 '14 at 22:45
  • For the record, the more elegant solution should be modified to be `[ qi::_val = px::lambda(_a = px::val(qi::_1))[arg1[_a]] ]` to work. Even granting all your cautions, I'm still wondering if the final form can't be described clearly enough as a boilerplate incantation to use reliably. E.g., `_val=lambda(_a=+_1)[arg1[_a]]` would be awfully useful, and memorizing seemingly arbitrary rules to not silently confuse pass by reference vs. value is endemic to C++ (E.g., passing arguments to threads). – user2913094 Sep 23 '14 at 22:58
  • @user2913094 Oops, those were copy-paste errors while editing the post only (you can see that in the Coliru link), fixed. Like I said, "if you like obscure code" you can do that variation. In practice, it's not about the legibility. It's about the odds of you missing a subtle interaction that leads to UB. I highly doubt you would have thought twice if [your compiler had seemingly done the right thing with just `lambda(_a=_1)[arg1[_a]]`](http://chat.stackoverflow.com/transcript/message/19046611#19046611). It's your call. _(My principles: Code should express intent. Verbosity can be good. KISS)_ – sehe Sep 23 '14 at 23:09
  • @user2913094 I am starting to believe that what we're doing is not supported by Phoenix, at all. I will have to revisit this to explain myself better. Stay tuned (and you can ping me if I forget) – sehe Sep 25 '14 at 07:01
  • You asked me to ping you for an update. Did I miss one? Thanks – user2913094 Oct 06 '14 at 12:53
  • @lol. Good point. Will be... Wednesday night CEST I reckon. Sorry for being so distracted :/ – sehe Oct 06 '14 at 12:54