Subject: [boost] Phoenix v3 review
From: Mathias Gaunard (mathias.gaunard_at_[hidden])
Date: 2011-02-24 11:52:29
Here is my review of Phoenix v3.
First, let me say that I am a big user of Phoenix v2, Spirit and Proto,
and that I therefore envision myself a big user of Phoenix v3 as well.
It is a great improvement on top of Phoenix v2 (in particular, no
strange bugs due to broken type deduction and correct usage of
result_of), and for that reason alone it warrants my approval.
So here it is: I vote yes for inclusion.
I however have concerns about its compatibility with Phoenix v2.
I tried to see how Spirit fared if I symlinked its underlying phoenix
directory to that of Phoenix v3, and it just doesn't work.
Mixing Phoenix v3 with Spirit doesn't work either.
I believe this is a very important issue. I'm not sure it needs to be
fixed before the first release of Phoenix as a first-class Boost
citizen, but it certainly needs to be fixed ASAP by porting Spirit to
use the new version.
Apart from that, the library is pretty satisfying:
- As I said, usage of result_of has been a great improvement, and bind
for example is now polymorphic and fully compatible with boost/std::bind.
- it supports perfect forwarding up to a limit
- It uses Proto in a minimalistic and modular way
- It is reasonably fast to compile, thanks to the instrumentation of
wave to preprocess headers. Compiling the STL module could be made
faster by using free functions instead of global objects (which should
be the recommended way to define lazy functions!).
- The docs haven't changed much, a few rectifications here and there
plus the addition of the new internals. It still contains typos and some
things that need to be updated.
Let's start on with more detailed remarks:
---- Phoenix v3 is missing a phoenix.hpp header file that includes all modules, which Phoenix v2 had (but in the parent directory). Lazy functions -------------- Using phoenix::function to turn a PFO into a lazy function is the most basic way to extend Phoenix with new functionality, and clearly the recommended way to do so according to the docs.   It is directly inherited from Phoenix v2, and only the result type deduction mechanism changed. This approach has several problems, at least in the way it is presented in the documentation. - global objects potentially increase binary size - those objects are not PODs and therefore requires runtime initialization, adding some runtime overhead at application startup - instantiation happens regardless of whether the function is used or not, which affects compilation time negatively. This method is massively used to define a whole lot of lazy functions that forward to standard algorithms and container functions , which suggests that this is indeed the recommended way to proceed. It should however be avoided; prefer defining a template function that constructs and applies the adapted function object, if the function object can stay a POD that's better too. I think it would also be valuable to add a "lazy" function (or some other name) that takes a PFO and returns its lazy version, that could be used inline in lambda expressions in a way similar to bind.  <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/starter_kit/lazy_functions.html>  <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/modules/bind.html>  <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/modules/stl/container.html> Phoenix as a Proto-based library -------------------------------- Extension of Phoenix is very nice: you can plug custom subgrammars and subnodes non-intrusively, making it very modular. Phoenix expressions are also refinements of Proto expressions, so I can use Proto transforms to alter the tree. It is also possible to specify custom evaluation strategies on a per-node basis, which paves the way for many exotic applications. Terminals can also be customized for evaluation within the default strategy. I'm not entirely sure this feature is not redundant, but I'm not familiar enough with the code to tell. Phoenix as Proto with statements -------------------------------- One thing I was hoping Phoenix v3 would be is Proto extended to support statements. While it comes pretty for some uses, it still fails one one point: the ability to define custom languages that embed Phoenix statements. The missing piece is handling of domains (extends are missing too, but I don't think they make sense at the statement level). I think that area needs some research though, so it's not relevant to acceptance at all. Documentation ------------- <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/modules/bind.html> says that bind is monomorphic. I thought it was polymorphic now? <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/actor.html> doesn't talk about perfect forwarding. <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/inside/actor.html> Actor cannot be both a concept and a model, it's a concept and a refinement. It would also be nice to highlight the difference with the PFO concept. "The problem is that given an arbitrary function F, using current C++ language rules, one cannot create a forwarding function FF that transparently assumes the arguments of F. " That's wrong. It's not impossible, it just requires an exponential number of overloads. C++0x rvalue references allow to reduce this to a linear amount. <http://svn.boost.org/svn/boost/sandbox/SOC/2010/phoenix3/libs/phoenix/doc/html/phoenix/inside/expression.html> spurious ] at the end. The layout of this section is weird, by the way. The macros appear on top but are not explained until a later page. I remember I saw a couple other typos and similar but I can't find them anymore. Anyway it needs some proofreading. Features I would like to see in a future version ------------------------------------------------- Ok, this has little to do in a review, but here it is anyway. I would like to have an adapter to turn a PFO into a monomorphic function object (i.e. a function object with a result_type) that can be passed to legacy algorithms. This can be done two ways: either by giving the return type or the type of the arguments explicitly. This could also be used to define variant visitors with lambdas. I would like it if Phoenix could detect function objects that are monomorphic and automatically propagate that monomorphism (but that's probably hard to do). I would like it if the polymorphic function objects generated by Phoenix were masked out using SFINAE if their body-expression would result in a hard error. Essentially, to do this, one needs to be able to test whether the default Proto transform of an expression leads to an error. While this should be possible with compilers supporting extended SFINAE, I haven't been able to grok the Proto internals well enough to do it yet myself.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk