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?
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)
is_odd
is truly polymorphic (as the functoris_odd_impl
). That isis_odd(_1)
can act on anything (that makes sense). For example inis_odd(_1)(2u)==true
andis_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:
but to create a Lambda "lazy function" we will have to choose one of the two:
Even if we define a template version
we will have to bind to a particular instance of the template function, for example
Neither
f1
norf2
norf3
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
templategeneric 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.)