5

How can I also include function bodies in a transformation of a Boost Phoenix expression?

For example, I have built on the Lazy Functions section of the Boost Phoenix Starter Kit, and created a lazy addition function:

struct my_lazy_add_impl {
  typedef int result_type;
  template <typename T>
  T operator()(T x, T y) const { return x+y; }
};
phoenix::function<my_lazy_add_impl> my_add;

I then prepare a simple plus-to-minus transform from a previous question, shown here:

struct invrt:
  proto::or_<
    proto::when<
      proto::plus<proto::_, proto::_>,
      proto::functional::make_expr<proto::tag::minus>(
        invrt(proto::_left), invrt(proto::_right)
      )
    >,
    proto::otherwise<
      proto::nary_expr<proto::_, proto::vararg<invrt> >
    >
  >
{};

However, when I apply an inverted Phoenix lambda expression, using my_add, to its arguments, as shown below, it seems the intended inversion has not been achieved. Is there a recommended way to implement function calls within Phoenix, which can facilitate such transformations?

int main(int argc, char *argv[])
{
  auto f =         phoenix::lambda(_a = 0)[my_add(_1,_2)];
  auto g = invrt()(phoenix::lambda(_a = 0)[my_add(_1,_2)]);
  std::cout << f()(1,2) << std::endl; // 3
  std::cout << g()(1,2) << std::endl; // 3 again; alas not -1
  return 0;
}
Community
  • 1
  • 1
user2023370
  • 10,488
  • 6
  • 50
  • 83

1 Answers1

3

The answer is really very simple and you're going to kick yourself. The expression transformation you wrote knows how to convert a plus node into a subtract node. But there is no plus node in the expression you're passing to it. Look again:

auto g = invrt()(phoenix::lambda(_a = 0)[my_add(_1,_2)]);

Where's the plus node? To Proto (and to Phoenix), my_add is opaque. They don't know there's an addition going on inside there. How could they?

====EDIT====

Consider this instead, which does what you intend:

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

auto const my_add = phoenix::let(_a = _1, _b = _2)[_a + _b];

struct invrt:
  proto::or_<
    proto::when<
      proto::plus<proto::_, proto::_>,
      proto::functional::make_expr<proto::tag::minus>(
        invrt(proto::_left), invrt(proto::_right)
      )
    >,
    proto::otherwise<
      proto::nary_expr<proto::_, proto::vararg<invrt> >
    >
  >
{};

int main()
{
  auto f =         phoenix::lambda(_a = 0)[my_add(_1,_2)];
  auto g = invrt()(phoenix::lambda(_a = 0)[my_add(_1,_2)]);

  std::cout << f()(1,2) << std::endl; // 3
  std::cout << g()(1,2) << std::endl; // -1, w00t!
}
Eric Niebler
  • 5,927
  • 2
  • 29
  • 43
  • Thankyou. I had guessed this was the way to do it, but wanted some reassurance before building on the idea. – user2023370 Mar 07 '13 at 10:39
  • p.s. I guess the `let` body could also be `_1 + _2`. – user2023370 Mar 07 '13 at 10:40
  • I /think/ you'll find that in more complicated contexts that you'll need to create a new scope for your function bodies; hence the `let`. At least, I seem to recall learning this lesson the hard way, but for the life of me, I can no longer remember why. :-P – Eric Niebler Mar 08 '13 at 08:00
  • Yes, I had the same feeling; why not just use `sequence` (?), as in: `auto const f2 = (stmt1,stmt2);`. I was leaning towards `lambda` over `let` but I am surprised now to see that my main reason for using scopes, that they will *return* the value of their last "statement", is also true of `sequence`. Hmm. Thanks again, I'll trust your judgement and go with scopes for the moment. – user2023370 Mar 08 '13 at 09:37
  • Then again, in favour of scopes, local variables are nice sometimes :) – user2023370 Mar 08 '13 at 09:51