In the Boost Phoenix article, "Transforming the Expression Tree", here, a set of specialisations of a custom invert_actions
class, are used to invert binary arithmetic expressions. For example a+b
becomes a-b
; a*b
becomes a/b
; and vice versa for both.
This involves a recursive traversal of the expression tree - however, this traversal stops when an expression involving an operator not explicitly handled is encountered. For example, _1+_2-_3
will become _1-_2+_3
, but _1+_1&_2
will stay as it is (there is no handler for &
). let(_a = 1, _b = 2) [ _a+_b ]
will also be left unchanged.
I had thought this was as intended by the article, but looking at the tests listed at the end, I see that if_(_1 * _4)[_2 - _3]
is expected to change; with the code supplied (here), I find that it doesn't.
How then can I define a generic Boost Phoenix expression tree transform which applies to all of a set of explicitly listed (n-ary) operators; leaving the others unchanged?
Some code may be useful. I'd like the following C++11 code (auto) to output 0
, and not 2
; without explicitly handling the &
, or any other operator/statement.
#include <iostream>
#include <boost/phoenix.hpp>
#include <boost/proto/proto.hpp>
using namespace boost;
using namespace proto;
using namespace phoenix;
using namespace arg_names;
struct invrt {
template <typename Rule> struct when : proto::_ {};
};
template <>
struct invrt::when<rule::plus>
: proto::call<
proto::functional::make_expr<proto::tag::minus>(
evaluator(_left, _context), evaluator(_right, _context)
)
>
{};
int main(int argc, char *argv[])
{
auto f = phoenix::eval( _1+_1&_2 , make_context(make_env(), invrt()) );
std::cout << f(1,2) << std::endl; // Alas 2 instead of 0
return 0;
}
This is how you do it with straight Proto:
#include <iostream>
#include <boost/phoenix.hpp>
#include <boost/proto/proto.hpp>
namespace proto = boost::proto;
using namespace boost::phoenix;
using namespace arg_names;
struct invrt:
proto::or_<
proto::when<
// Turn plus nodes into minus
proto::plus<proto::_, proto::_>,
proto::functional::make_expr<proto::tag::minus>(
invrt(proto::_left), invrt(proto::_right)
)
>,
proto::otherwise<
// This recurses on children, transforming them with invrt
proto::nary_expr<proto::_, proto::vararg<invrt> >
>
>
{};
int main(int argc, char *argv[])
{
auto f = invrt()(_1+_1&_2);
proto::display_expr(f);
std::cout << f(1,2) << std::endl;
return 0;
}
Phoenix has layered a bunch of stuff on top of Proto. I don't know the semantics of pheonix::eval
or why what you tried didn't work. Perhaps someone knowledgeable of Phoenix will chime in.
==== EDIT ====
I figured out the problem with the Phoenix example. It's not recursing for the non-plus case. Your code should be as follows:
#include <iostream>
#include <boost/phoenix.hpp>
#include <boost/proto/proto.hpp>
using namespace boost;
using namespace proto;
using namespace phoenix;
using namespace arg_names;
struct invrt {
template <typename Rule>
struct when :
// NOTE!!! recursively transform children and reassemble
nary_expr<_, vararg<proto::when<_, evaluator(_, _context)> > >
{};
};
template <>
struct invrt::when<rule::plus> :
proto::call<
proto::functional::make_expr<proto::tag::minus>(
evaluator(_left, _context), evaluator(_right, _context)
)
>
{};
int main()
{
auto f = phoenix::eval( _1+_1&_2 , make_context(make_env(), invrt()) );
display_expr(f);
std::cout << f(1,2) << std::endl; // Prints 0. Huzzah!
}
Whether you consider that simpler or more complicated than the straight Proto solution is for you to decide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With