Boost logo

Boost Users :

Subject: Re: [Boost-users] [PROTO] Custom functions, scalar terminal and make_expr vs details::as_expr_if
From: Eric Niebler (eric_at_[hidden])
Date: 2008-11-29 16:01:59


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

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net