Boost logo

Boost :

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

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk