Boost logo

Boost :

From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2008-04-02 06:02:06


On Wed, Apr 2, 2008 at 6:18 AM, shunsuke <pstade.mb_at_[hidden]> wrote:
> shunsuke wrote:
> > If fold_ is DefaultConstructible and a default-constructed one is callable,
> > it would be:
> >
> > typedef
> > static_< mpl::always<
> > return_of<T_regular(fold_(reverse_(T_bll_1), T_bll_2, T_bll_3))>::type
> > > >
> > reverse_fold;
>
> Sorry, this never works. T_regular was pointless.

May be I shouldn't have used regular, but I want a way to stop a
lambda expression from being, well, a lambda expression (so that I can
compose it).

I'll give you my use cases:

In my framework (which unfortunately I do not own), I can define a
fold function like this (BTW, such a macro would be a great addition
to Egg):

DEFUN(fold, (range)(init)(op), *(init) ){
    // you can access the argument types via range_type, init_type and op_type.
    // result_type here is automagically defined as
result_of<fold(range_type&, init_type&, op_type&)>::type
   result_type result = std::accumulate(boost::begin(range),
boost::end(range), init, op);
   return result_type;
}

[the syntax is shamelessly stolen from boost.parameters]
This will define a function object type named 'fold' in namespace
'meta' called range and an instance of this type, also named 'fold',
in namespace 'algo' (the namespace names are more or less for
historical reasons and do not really make much sense; also making this
work in the face of ADL is a pain).

Note that the third argument of the macro defines the result type. If
you prefix it with a '*', you can pass an MPL expression instead of an
actual type: in this context 'init' is the same as mpl::_2 (the macro
takes care to rename the placeholders as the argument types).

'defun' does automatically perfect forwarding of its arguments as you
would expect, and of course is both result_of and lambda compatible.
It can also automatically participate in pipelines. Finally you can
use the same object as a normal function object and in lambda
expressions without the need for bind:

normal function (non lazy):

   fold(my_range, 0, plus);

used in a lambda expression (the first fold is not lazy, the second is lazy):

   fold(my_range_of_range, 0, lambda[ fold(_2, _1, lambda[ _1 + _2]) ] );

The library uses some magic to figure out if you want a lazy fold or
not. Note that if you pass a lambda expression
to fold, you must always wrap it with 'protect':

  fold(my_range, 0, _1 + _2) ; // illegal: the whole statement is a
lazy expression and is not executed!

  fold(my_range, 0, protect(_1 + _2)); // good, now the scope for _1
and _2 is limited. The call to 'fold' is not lazy.

  // same as above, but I find it more readable. Also makes the lambda
result_of
  // compatible and 'regular' (also does perfect forwarding with old
versions of
  // boost.lambda).
  fold(my_range, 0, lambda[ _1 + _2]);

I'm not saying that egg should support optionally-lazy functions [1],
it is ok if the lazy and non lazy variant have the same name. I just
want to be sure it is easy to define them.

> After all, Boost.Lambda bind and placeholders are not usable here.

Why? Detail please :)

> In fact, Egg secretly has its own usable placeholders, though.

Secretly? You mean that T_bll_1 and firends are not really
boost.lambda placeholder types? Or you have something else in mind?
Please elaborate...

[1] A reason not to have optionally lazy functions is that if you
forgot to protect a lambda, the entire expression might become a big
lambda expression. OTOH, except for a top level for each, I always use
the result of my function objects as they are otherwise side-effect
free, so such errors would be easily caught by the compiler (result
type mismatch).

-- 
gpd

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