
Joel Falcou wrote:
I have a large number of custom unary and bin ary function that I map using unary_expr and binary_expr. The problem is the following : my grammar has integral or floating point scalar terminal. So, when I want to create a binary function that takes any elements of my domain, I'm boudn to do the following :
template
typename result_of::make_expr<....>::type my_custom_binary_func( L const& l, R const& r ) { return make_expr( ... ); } Alas, as I already discussed with Eric, nothign prevent me to use my_custom_binary_func over types that aren't in my grammar.
The first question to ask is if it is really absolutely necessary to prevent users from creating invalid expressions with my_custom_binary_func(), or if you can catch the error someplace else; e.g., when users try to evaluate the expression. Reporting a hard error later can often yield a better error message then using SFINAE to prune overload sets earlier.
To do so, I was told to use SFINAE to prune unwanted element by matching the result type of make_expr on my grammar. However, this proves cumbersome and slow down compile time by *a large factor* when I have a large number of such function (and I have like 50 or 60).
So i wandered into proto source code and looked at how proto solves this problem for its binary operator. This solution seems more elegant but as it live sin proto::detail, I don't think I'm intended to use it.
So, how can I have a simple and fast creation of custom expression ? Am I missing something blatant ?
If you really want to prevent users from creating invalid expressions
with these functions, you can do something like the following ...
template<typename T>
struct valid_terminal {}; // empty!
template<>
struct valid_terminal<int> { typedef int type; };
// other specializations for valid terminal types ...
// When L and R are both proto expressions
template
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When L is a proto expression and R is a terminal
template
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When R is a proto expression and L is a terminal
template
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When L and R are valid terminal types
template
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
If you want to improve compile times even more, you can avoid make_expr
entirely and code by-hand what make_expr would do. So, e.g., the second
overload above would be:
// When L is a proto expression and R is a terminal
template
my_custom_binary_func( L const& l, R const& r ) { /*...*/ } And in the body of my_custom_binary_func() you initialize an object of this type directly instead of calling make_expr(). Naturally, you'd put all this gunk in a macro so you can easily define all 50 or so of your custom binary functions. Note that I don't think this solution is particularly better than just letting my_custom_binary_func create invalid expressions and catching the mistake later. HTH, -- Eric Niebler BoostPro Computing http://www.boostpro.com