0

I would expect the following example Boost Phoenix expression to compile.

What am I missing?

int plus(int a,int b)
{
    return a+b;
}

void main(int argc,char** argc)
{
    auto plus_1 = phx::bind(&plus,1,arg1);
    auto value  = phx::lambda[phx::val(plus_1)(arg1)]()(1);
    std::cout << value << std::endl;
}
Ted
  • 14,465
  • 6
  • 28
  • 28

3 Answers3

4
auto plus_1 = phx::bind(&plus,1,arg1);

After this line, plus_1 is a function object that takes one int argument and adds one to it.

phx::lambda[plus_1(arg1)](1);

Whoops. This isn't going to work because (as we said above) plus_1 is a function object that takes one int argument and adds one to it. Here, you're trying to invoke it with arg1.

It isn't obvious from your code what you expect it to do. Can you clarify?

====EDIT====

I see you've edited the code in your question. Your code is still wrong but for a different reason now. This:

phx::val(plus_1)(arg1)

... uses val to create a nullary function that returns the plus_1 unary function. You then try to invoke the nullary function with arg1. Boom.

Here is code that executes and does (what I believe) you intend:

#include <iostream>
#include <boost/phoenix/phoenix.hpp>
namespace phx = boost::phoenix;
using phx::arg_names::arg1;

int plus(int a,int b)
{
    return a+b;
}

int main()
{
    auto plus_1 = phx::bind(&plus, 1, arg1);
    int value = phx::bind(phx::lambda[plus_1], arg1)(1);
    std::cout << value << std::endl;
}

The first bind takes the binary plus and turns it into a unary function with the first argument bound to 1. The second bind creates a new unary function that is equivalent to the first, but it does so by safely wrapping the first function using lambda. Why is that necessary? Consider the code below, which is equivalent, but without the lambda:

// Oops, wrong:
int value = phx::bind(phx::bind(&plus, 1, arg1), arg1)(1);

Notice that arg1 appears twice. All expressions get evaluated from the inside out. First, we'll bind the inner arg1 to 1, then evaluate the inner bind yielding 2, which we then try to bind and invoke. That's not going to work because 2 isn't callable.

The use of lambda creates a scope for the inner arg1 so it isn't eagerly substituted. But like I said, the use of the second bind, which forces the need for lambda, yields a function that is equivalent to the first. So it's needlessly complicated. But maybe it helped you understand about bind, lambda and Phoenix scopes.

Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • Thank you for the explanation. My intent to to understand Phoenix better. I find bind to be confusing based on the documentation. – Ted Oct 04 '12 at 17:32
  • What I don't understand is why you need to wrap the lambda with a bind expression. What is preventing the lambda from living on it's own. Is it because a PHX expression is really a lambda? Thanks a again for the answer. – Ted Oct 04 '12 at 18:10
  • Ok I think i get it. Basically function object are not Actors and bind does not return and Actor but a function object.The lambda it needed to wrap the function object with and Actor for it to be lazy evaluated. – Ted Oct 04 '12 at 18:29
  • No, `bind` expressions return Actors. Actors _are_ function objects. You don't need to wrap the `lambda` in a `bind`. You can do this, for instance: `int value = phx::lambda[plus_1]()(1)`. `lambda` is like a level of indirection; evaluate it once to get the body of the lambda, then evaluate it again to get the final result. – Eric Niebler Oct 05 '12 at 07:01
2

It's not clear to me what you're trying to accomplish by using lambda here, but if you just want to call plus_1 with 1 (resulting in 2), it's much simpler than your attempt:

#include <iostream>
#include <boost/phoenix.hpp>

int plus(int a, int b)
{
    return a + b;
}

int main()
{
    namespace phx = boost::phoenix;

    auto plus_1 = phx::bind(plus, 1, phx::arg_names::arg1);
    std::cout << plus_1(1) << '\n';
}

Online demo

If this isn't what you're trying to accomplish, then you need to describe what you actually want. :-]

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • I would expect my expression to work. It's not that I am trying accomplish adding numbers but understand why lamba does not do what I would expect. – Ted Sep 28 '12 at 23:10
1

Perhaps this can explain it better.

Phoenix is not magic; it is first and foremost C++. It therefore follows the rules of C++.

phx::bind is a function that returns a function object, an object which has an overloaded operator() that calls the function that was bound. Your first statement stores this object into plus_1.

Given all of this, anytime you have the expression plus_1(...), this is a function call. That's what it is; you are saying that you want to call the overloaded operator() function on the type of that object, and that you are going to pass some values to that function.

It doesn't matter whether that expression is in the middle of a [] or not. phx::lambda cannot make C++ change its rules. It can't make plus_1(...) anything other than an immediate function call. Nor can arg1 make plus_1(...) not an immediate function call.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982