Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-04-05 20:45:42


Peter Dimov wrote:
> Eric Niebler:
>> So where have I screwed up my logic?
>
> Nowhere. But I don't think that this problem is somehow caused by result_of.
> If you replace every result_of occurence with <magic happens here> the
> result would be the same.
>
> Can you give an example in which compose<identity,make_tuple>, implemented
> within your new framework, is able to avoid the problem?

OK, scratch everything and lets get back to basics. I'm finding I just
don't know how to write a TR1 function object in C++03. I need help.

== Background ==

My case study is proto's transforms. There are function objects usable
within transforms for (a) creating new expressions (rvalues) and (b)
accessing the parts of nodes (which may be rvalues or lvalues).

So for instance there is make_plus, which creates a new plus node and
returns an rvalue. And there is _ which returns the the current
expression (rvalue or lvalue). These are function object types. Proto
defines a DSEL that lets users compose function object types using
"round" lambdas: function types. So Proto treats this

   make_plus(_,_)

as a composite function object type. There is a piece of code in Proto
that interprets "make_plus(_,_)" as a function object. Ostensibly, if
the current expression were an object "e" of type "E const &", then
Proto would compute the return type as:

   result_of<make_plus(
     result_of<_(E const &)>::type
    ,result_of<_(E const &)>::type
   )>::type

And it would invoke invoke it as if it were:

   return make_plus()( _()(e), _()(e) );

So now how are _ and make_plus defined?

_ is simple -- it is essentially identity which I showed earlier.

make_plus is trickier. It must be optimal and safe. When passed lvalues,
it should return a new node that holds the children by reference. When
passed rvalues, it should return a new node that hold the children by value.

== Problem ==

Here's the thing ... I have no idea how to implement make_plus as a TR1
function object. Obviously, it's operator() can't know in general
whether it is being passed rvalues or lvalues. I can play games with
reference_wrappers but that's really just an arbitrary rule, and not one
in everybody's rule book, AFAIK. Proto's transform system is open --
user's can extend the system by writing their own function objects -- or
simply by using existing function objects as-is. I don't want to force
them to accept the reference_wrapper convention.

In contrast, the following would Just Work:

struct make_plus
{
   template<typename Sig> struct impl;

   template<typename This, typename Left, typename Right>
   struct impl< This(Left, Right) >
   {
     // Left and Right are allowed to be reference types
     typedef plus<Left, Right>::type result_type;

     result_type operator()(
       typename add_const_ref<Left>::type left
      ,typename add_const_ref<Right>::type right
     ) const
     {
       return result_type(left, right);
     }
   };
};

struct _
{
   template<typename Sig> struct impl;

   template<typename This, typename Arg>
   struct impl< This(Arg) >
   {
     typedef Arg result_type;

     result_type operator()(
       typename add_const_ref<Arg>::type arg
     ) const
     {
       return arg;
     }
   };
};

Now, when Proto sees a binary composite transform like make_plus(_,_) it
computes the return type as:

make_plus::impl<
   _::impl<Expr const &>::result_type
, _::impl<Expr const &>::result_type
>::result_type

And to invoke it, it essentially does this:

make_plus::impl<
   _::impl<Expr const &>::result_type
, _::impl<Expr const &>::result_type
>()(
   _::impl<Expr const &>()(e)
, _::impl<Expr const &>()(e)
)

This is very ugly, but it's hidden deep inside Proto so nobody has to
see it. The benefit is that rvalue/lvalue-ness of arguments is not lost,
and it is expressed in a very natural way -- with the presence or
absence of a reference qualifier.

I still can't see how to make this work with TR1-style function objects
without forcing everybody in the world to write function objects that
accept reference_wrapper to mean "lvalue".

-- 
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