Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2005-10-07 08:40:49


Geoffrey Romer <geoff.romer_at_[hidden]> writes:

> On 10/6/05, David Abrahams <dave_at_[hidden]> wrote:
>> Geoffrey Romer <geoff.romer_at_[hidden]> writes:
>>
>> > On 10/6/05, David Abrahams <dave_at_[hidden]> wrote:
>> >
>> >> Oh. Maybe mapping into the runtime world will help. If you think of
>> >> metafunc as a regular function, then "metafunc<args...>::type" is
>> >> equivalent to a regular function call, and "metafunc<args...>" is
>> >> equivalent to boost::bind(func, args...)
>> >>
>> >> These are exact analogies, AFAICT.
>> >
>> > OK, that's what I thought, but it seems not to apply to
>> > lambda-expressions. If I want to call foo<> on a placeholder
>>
>> I don't know what you mean, but I'm pretty sure you don't mean what
>> you said. Nobody but the MPL internals want to actually invoke
>> metafunctions on placeholders. Very often that will cause an error,
>> because the placeholder does not meet the expectations of the
>> metafunction for the type that is actually supposed to be passed to
>> it.
>
> Let me put it like this: suppose I have some metaprogramming expression like
>
> typename foo<typename bar<baz>::type >::type
>
> Now suppose I want to apply this operation to a sequence of types,
> rather than to baz, using transform<>. To do this, I need to convert
> it to a lambda-expression to pass to transform<>. Intuitively, I would
> expect this to be done by replacing baz with _1:
>
> typename foo<typename bar<_1>::type >::type
>
> But this doesn't compile for the reasons you explain. You seem to be
> telling me to eliminate the typename...::type on metafunction calls
> that contain actual placeholders as arguments (and in practice this
> seems to work), which leads to:
>
> typename foo<bar<_1> >::type
>
> I take it that's correct? If so, suppose now that I have another piece of code:

No, that won't work. There's still a direct invocation there. Take
this as a general rule: you never want to ask for the nested ::type of
a template that has a placeholder in any of its arguments, unless that
template is specifically designed to operate on lambda expressions.
I assume foo is not one of those. To translate the above into a
lambda expression:

              foo<bar<_1> >

> typename foo<bar<baz> >::type
>
> In other words, "apply foo to the nullary metafunction bar<baz>". Now
> suppose again that I want to turn this into an equivalent
> lambda-expression. Intuitively, I would again expect to do this by
> substituting _1 for baz:
>
> typename foo<bar<_1> >::type.
>
> However, this is identical to the lambda-expression for my first
> example, which has different semantics, so one of the two must be
> wrong. Which is wrong, and how can I fix it?

They're both wrong. The first is wrong for the reasons already
described. The second is wrong for the same reasons, and even after
you strip the ::type, you have other problems:

The MPL lambda expression mechanism will recognize bar<_1>
as something that demands substitution, perform the substitution, then
look for a nested ::type. When it finds that (it will, since bar is a
unary metafunction), that ::type will be passed on to foo.

AFAIK, the only way to get that case to work is to write a new
metafunction that does what you want:

   template<class T>
   struct foo_of_lazy_bar
   {
       // a nullary metafunction
       typedef bar<T> lazy_bar;

       // apply foo to that nullary metafunction
       typedef typename foo<lazy_bar>::type
   };

or, more simply:

   template<class T>
   struct foo_of_lazy_bar
     : foo<bar<T> >
   {};

Now foo_of_lazy_bar<_1> does what you want.

> Thanks for all your help, by the way. I'm being a bit stubborn about
> this because I want to make sure I really know what's going on here.

You're welcome.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.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