Proto expression types are PODs (Plain Old Data), and do not have constructors. They are brace-initialized, as follows: terminal<int>::type const _i = {1};
The reason is so that expression objects like Anyone who has peeked at Proto's source code has probably wondered, "Why all the dirty preprocessor gunk? Couldn't this have been all implemented cleanly on top of libraries like MPL and Fusion?" The answer is that Proto could have been implemented this way, and in fact was at one point. The problem is that template metaprogramming (TMP) makes for longer compile times. As a foundation upon which other TMP-heavy libraries will be built, Proto itself should be as lightweight as possible. That is achieved by prefering preprocessor metaprogramming to template metaprogramming. Expanding a macro is far more efficient than instantiating a template. In some cases, the "clean" version takes 10x longer to compile than the "dirty" version. The "clean and slow" version of Proto can still be found at http://svn.boost.org/svn/boost/branches/proto/v3. Anyone who is interested can download it and verify that it is, in fact, unusably slow to compile. Note that this branch's development was abandoned, and it does not conform exactly with Proto's current interface.
Much has already been written about dispatching on type traits using SFINAE
(Substitution Failure Is Not An Error) techniques in C++. There is a Boost
library, Boost.Enable_if, to make the technique idiomatic. Proto dispatches
on type traits extensively, but it doesn't use
Consider the implementation of template<typename T> struct is_expr : is_base_and_derived<proto::some_expr_base, T> {}; Rather, it is implemented as this: template<typename T, typename Void = void> struct is_expr : mpl::false_ {}; template<typename T> struct is_expr<T, typename T::proto_is_expr_> : mpl::true_ {};
This relies on the fact that the specialization will be preferred if
Why does Proto do it this way? The reason is because, after running extensive
benchmarks while trying to improve compile times, I have found that this
approach compiles faster. It requires exactly one template instantiation.
The other approach requires at least 2:
In several places, Proto needs to know whether or not a function object
Another way of framing the question is by trying to implement the following
template<typename Fun, typename A, typename B> struct can_be_called;
First, we define the following struct dont_care { dont_care(...); }; We also need some private type known only to us with an overloaded comma operator (!), and some functions that detect the presence of this type and return types with different sizes, as follows: struct private_type { private_type const &operator,(int) const; }; typedef char yes_type; // sizeof(yes_type) == 1 typedef char (&no_type)[2]; // sizeof(no_type) == 2 template<typename T> no_type is_private_type(T const &); yes_type is_private_type(private_type const &); Next, we implement a binary function object wrapper with a very strange conversion operator, whose meaning will become clear later. template<typename Fun> struct funwrap2 : Fun { funwrap2(); typedef private_type const &(*pointer_to_function)(dont_care, dont_care); operator pointer_to_function() const; };
With all of these bits and pieces, we can implement template<typename Fun, typename A, typename B> struct can_be_called { static funwrap2<Fun> &fun; static A &a; static B &b; static bool const value = ( sizeof(no_type) == sizeof(is_private_type( (fun(a,b), 0) )) ); typedef mpl::bool_<value> type; };
The idea is to make it so that
We wrap
The function pointer can accept any two arguments by virtue of the
If there is a better option --- for example if
Notice how
This should also make plain the purpose of the overloaded comma operator
in That's how it works with binary functions. Now repeat the above process for functions up to some predefined function arity, and you're done. I'd like to thank Joel de Guzman and Hartmut Kaiser for being willing to take a chance on using Proto for their work on Spirit-2 and Karma when Proto was little more than a vision. Their requirements and feedback have been indespensable. Thanks also to the developers of PETE. I found many good ideas there. |