![]() |
Home | Libraries | People | FAQ | More |
Transforms are typically of the form proto::when< Something, R(A0,A1,...) >
.
The question is whether R
represents a function to call or an object to construct, and the answer
determines how proto::when<>
evaluates the transform.
proto::when<>
uses the proto::is_callable<>
trait to disambiguate between the two. Proto does its best to guess whether
a type is callable or not, but it doesn't always get it right. It's best
to know the rules Proto uses, so that you know when you need to be more
explicit.
For most types R
, proto::is_callable<R>
checks for inheritance from proto::callable
.
However, if the type R
is a template specialization, Proto assumes that it is not
callable even if the template inherits from proto::callable
.
We'll see why in a minute. Consider the following erroneous callable
object:
// Proto can't tell this defines something callable! template<typename T> struct times2 : proto::callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // ERROR! This is not going to multiply the int by 2: struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};
The problem is that Proto doesn't know that times2<int>
is callable, so rather that invoking
the times2<int>
function object, Proto will try to construct a times2<int>
object and initialize it will an
int
. That will not compile.
![]() |
Note |
---|---|
Why can't Proto tell that |
There are a couple of solutions to the times2<int>
problem. One solution is to wrap
the transform in proto::call<>
. This forces Proto to treat
times2<int>
as callable:
// OK, calls times2<int> struct IntTimes2 : proto::when< proto::terminal<int> , proto::call<times2<int>(proto::_value)> > {};
This can be a bit of a pain, because we need to wrap every use of times2<int>
,
which can be tedious and error prone, and makes our grammar cluttered
and harder to read.
Another solution is to specialize proto::is_callable<>
on our times2<>
template:
namespace boost { namespace proto { // Tell Proto that times2<> is callable template<typename T> struct is_callable<times2<T> > : mpl::true_ {}; }} // OK, times2<> is callable struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};
This is better, but still a pain because of the need to open Proto's namespace.
You could simply make sure that the callable type is not a template specialization. Consider the following:
// No longer a template specialization! struct times2int : times2<int> {}; // OK, times2int is callable struct IntTimes2 : proto::when< proto::terminal<int> , times2int(proto::_value) > {};
This works because now Proto can tell that times2int
inherits (indirectly) from proto::callable
.
Any non-template types can be safely checked for inheritance because,
as they are not templates, there is no worry about instantiation errors.
There is one last way to tell Proto that times2<>
is callable. You could add an
extra dummy template parameter that defaults to proto::callable
:
// Proto will recognize this as callable template<typename T, typename Callable = proto::callable> struct times2 : proto::callable { typedef T result_type; T operator()(T i) const { return i * 2; } }; // OK, this works! struct IntTimes2 : proto::when< proto::terminal<int> , times2<int>(proto::_value) > {};
Note that in addition to the extra template parameter, times2<>
still inherits from proto::callable
.
That's not necessary in this example but it is good style because any
types derived from times2<>
(as times2int
defined above) will still be considered callable.