Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the benefits of using Boost.Phoenix?

I can not understand what the real benefits of using Boost.Phoenix.

When I use it with Boost.Spirit grammars, it's really useful:

double_[ boost::phoenix::push_back( boost::phoenix::ref( v ), _1 ) ]

When I use it for lambda functions, it's also useful and elegant:

boost::range::for_each( my_string, if_ ( '\\' == arg1 ) [ arg1 = '/' ] );

But what are the benefits of everything else in this library? The documentation says: "Functors everywhere". I don't understand what is the good of it?

like image 990
Denis Shevchenko Avatar asked Feb 16 '11 07:02

Denis Shevchenko


2 Answers

I'll point you out what is the critical difference between Boost.Lambda and Boost.Phoenix:

Boost.Phoenix supports (statically) polymorphic functors, while Boost.Lambda binds are always monomorphic.

(At the same time, in many aspects the two libraries can be combined, so they are not exclusive choices.)

Let me illustrate (Warning: Code not tested.):

Phoenix

In Phoenix a functor can converted into a Phoenix "lazy function" (from http://www.boost.org/doc/libs/1_54_0/libs/phoenix/doc/html/phoenix/starter_kit/lazy_functions.html)

struct is_odd_impl{
    typedef bool result_type; // less necessary in C++11
    template <typename Arg>
    bool operator()(Arg arg1) const{
        return arg1 % 2 == 1;
    }
};

boost::phoenix::function<is_odd_impl> is_odd;

is_odd is truly polymorphic (as the functor is_odd_impl). That is is_odd(_1) can act on anything (that makes sense). For example in is_odd(_1)(2u)==true and is_odd(_1)(2l)==true. is_odd can be combined into a more complex expression without losing its polymorphic behavior.

Lambda attempt

What is the closest we can get to this in Boost.Lambda?, we could defined two overloads:

bool is_odd_overload(unsigned arg1){return arg1 % 2 == 1;}
bool is_odd_overload(long     arg1){return arg1 % 2 == 1;}

but to create a Lambda "lazy function" we will have to choose one of the two:

using boost::lambda::bind;
auto f0 = bind(&is_odd_overload, _1); // not ok, cannot resolve what of the two.
auto f1 = bind(static_cast<bool(*)(unsigned)>(&is_odd_overload), _1); //ok, but choice has been made
auto f2 = bind(static_cast<bool(*)(long)>(&is_odd_overload), _1); //ok, but choice has been made

Even if we define a template version

template<class T>
bool is_odd_template(T arg1){return arg1 % 2 == 1;}

we will have to bind to a particular instance of the template function, for example

auto f3 = bind(&is_odd_template<unsigned>, _1); // not tested

Neither f1 nor f2 nor f3 are truly polymorphic since a choice has been made at the time of binding.

(Note1: this may not be the best example since things may seem to work due to implicit conversions from unsigned to long, but that is another matter.)

To summarize, given a polymorphic function/functor Lambda cannot bind to the polymorphic function (as far as I know), while Phoenix can. It is true that Phoenix relies on the "Result Of protocol" http://www.boost.org/doc/libs/1_54_0/libs/utility/utility.htm#result_of but 1) at least it is possible, 2) This is less of a problem in C++11, where return types are very easy to deduce and it can be done automatically.

In fact, in C++11, Phoenix lambdas are still more powerful than C++11 built-in lambdas. Even in C++14, where template generic lambdas are implemented, Phoenix is still more general, because it allows a certain level of introspection. (For this an other things, Joel de Guzman (developer of Phoenix) was and still is well ahead of his time.)

like image 119
alfC Avatar answered Nov 02 '22 20:11

alfC


Well, its a very powerful lambda language.

I used it to create a prototype for a math-like DSL:

http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/interval.hpp

and many other things:

http://code.google.com/p/asadchev/source/browse/#svn%2Ftrunk%2Fprojects%2Fboost%2Fphoenix

like image 39
Anycorn Avatar answered Nov 02 '22 20:11

Anycorn