
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<class L,class R> 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<class L,class R> typename result_of::make_expr< my_custom_binary_tag , my_domain , typename L::proto_derived_type const & , typename R::proto_derived_type const &
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When L is a proto expression and R is a terminal template<class L,class R> typename result_of::make_expr< my_custom_binary_tag , my_domain , typename L::proto_derived_type const & , typename valid_terminal<R>::type const &
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When R is a proto expression and L is a terminal template<class L,class R> typename result_of::make_expr< my_custom_binary_tag , my_domain , typename valid_terminal<L>::type const & , typename R::proto_derived_type const &
::type my_custom_binary_func( L const& l, R const& r ) { /*...*/ }
// When L and R are valid terminal types template<class L,class R> typename result_of::make_expr< my_custom_binary_tag , my_domain , typename valid_terminal<L>::type const & , typename valid_terminal<R>::type const &
::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<class L,class R> my_expr_wrapper< proto::expr< my_custom_binary_tag , proto::list2< typename L::proto_derived_type const & my_expr_wrapper< proto::expr< proto::tag::terminal , proto::term< typename valid_terminal<R>::type const & > > > > >
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