$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Eric Niebler (eric_at_[hidden])
Date: 2007-05-09 01:58:29
Tough questions. You've touched on a weakness of proto. I hope to make 
improvements in this area after BoostCon ...
Maurizio Vitale wrote:
> Suppose I needed a new tag for representing some high-level concept that I do not want
> to express in terms of C++ operators [it could be a get_bit(N) operation that has very
> different implementation when for my numbers the unserlyining implementation is a
> builtin type or a GMP big int]
> 
> Here's my code for adding a binary operator my_plus:
> 
>     struct my_plus {};
> 
>     template<typename Left, typename Right>
>     proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > const
>     make_my_plus_expr(Left& left, Right& right)
>     {
>       typedef proto::expr<my_plus, proto::args2<proto::ref_<Left>, proto::ref_<Right> > > expr_type;
>       expr_type that={left,right};
>       return proto::generate<typename Left::domain, expr_type>::make (that);
>     }
> 
> Questions:
> 
>   - is the above the right way, or is there some friendlier interface?
An improvement to the return type would be:
proto::expr<
     my_plus
   , proto::args2<
         typename proto::as_arg<Left>::type
       , typename proto::as_arg<Right>::type
     >
 > const
proto::as_arg<> has the job of wrapping proto types in ref_<>, and 
non-proto types in terminal<>::type.
> 
>   - if I wanted to support the case where left and/or right are not proto
>     expressions, it would be enough to call generate<>::make on the non
>     proto-ized one(s) to turn them into proto::terminal, right?
>     Maybe proto could provide a mechanism for doing this, seems common
>     enough [might be enough to raise as_expr_if2 from proto::detail].
Yes, I think you're right. Some high-level wrapper like as_expr_if2 is 
needed. Essentially, you'll need something like:
typename my_generate<
     typename my_domain_of<Left>::type
   , typename my_domain_of<Right>::type
   , proto::expr< /*see above*/ >
 >::type const
where my_domain_of<X> returns the domain of X or proto::default_domain 
if X if not a proto type, and my_generate<> checks that the domains are 
compatible (the same, or else one is the default_domain) and that the 
proto::expr< /**/ > is a valid expr for that domain.
This is, sadly, an exercise for the reader at this point. As is 
extending this to N arguments.
>     But clearly then people like me will ask for N up to BOOST_PROTO_MAX_ARITY.
>     Maybe a separate CPP constant could be used to keep the number of permutations low.
>     Or deal separately with >2 args with an mpl (or fusion) sequence.
>     Pretty please...
Well, there's unpack_expr() in proto/make_expr.hpp which unpacks a 
fusion sequence into a proto expression. But it's pretty half-baked at 
the moment. This needs work.
>     [btw, if I understand the code in operators.hpp, proto doesn't explicitely
>      check that the domains of the two operands are the same. This situation
>      doesn't seem really supported, nor desirable, and maybe code doing it wouldn't
>      compile anyhow. But in case it did compile and do wrong things you might
>      consider slowing down compilation and adding a check]
Line 95 in operators.hpp has the check you're looking for.
>   - is it correct that all I have to do then is to either:
>        - make my_plus disappear before evaluation (via transforms), or
>        - provide an evaluation context with the appropriate overload
>          for operator()(my_plus, ...). For instance the following would give
>          to my_plus the normal meaning of '+':
> 
>           template<typename Left, typename Right>
>           double operator() (my_plus, const Left& left, const Right& right) const{
>             return proto::eval(left, *this) + proto::eval (right, *this);
>           }
That's right.
>   - a simple example for this would be very nice for the documentation.
Agreed.
-- Eric Niebler Boost Consulting www.boost-consulting.com