Boost logo

Boost :

From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2002-09-19 16:35:31


Terje Slettebø wrote:
> In the posting with my "factorial" metafunction
> (http://aspn.activestate.com/ASPN/Mail/Message/1316005), I
> propose to make the metafunctions evaluate their own arguments
> (apply "::type" to them), just like it happens in run-time functions.
> With that, the factorial example becomes (quoting from my posting):
>
> template<class V>
> struct factorial
> {
> typedef typename V::type Value;
>
> typedef typename apply_if<equal_to<Value,value<Value,0> >,
> value<Value,1>,
> mul<Value,factorial<prior<Value> > >
> >::type type;
> }
>
> The first typedef evaluates the metafunction argument, V.
>
> "value" is another new metafunction. It's a way to provide a generic
> constant, of the type given in the first argument, and the
> value given in the second. Thus, if "Value" is int_c<...>, then
> "value<Value,1>::type" is int_c<1>.

mpl::integral_c<..>?

[..]

> If the metafunctions, like plus, evaluated their own arguments, as
> suggested, you could write it like this [Addition: I see that
> you've come to this, yourself, too, later here]:
>
> apply_if< some_condition,
> some_type_1,
> plus< some_type_2, some_number >
> >::type
>
> This avoids evaluating some_type_2, unless that part of the
> if-branch is taken. Note that for this to work, all values must have
> a "type" member, which is the case in MPL. If a type isn't a
> metafunction, "type" simply provides the identity, the same type.
> So "::type" may be applied unconditionally on all types.
>
> I found that such a change of MPL would greatly simplify
> writing code for it, as you avoid having to create ad-hoc templates
> (like your "recurse", above), just to avoid premature instantiation
> (evaluation) of some_type_2.
> Thus, you can write the code in a natural way, not having to
> spread it out.
> I found this spreading out to be similar to how template
> specialisations spreads code, which is what functions like apply_if
> is meant to avoid. You also avoid all those "::type"s in the code.

I considered and re-considered the idea a couple of times during MPL
development. It doesn't really work well; in particular, it conflicts with
the notion of nullary metafunctions, pretty much making the library useless
for manipulating the latter ones:

    // build a table of nullary metafunctions for later
    // (selective) invocation
    typedef push_back< f_table0,my_f<arg1,arg2> >::type f_table1;
    typedef push_back< f_table1,her_f<arg3> >::type f_table2;
    ...

It's not hard to see that if 'push_back' is implemented along the lines of
the proposed idea

    template<
        typename Sequence
        , typename T
>
    struct push_back
        : push_back_traits< typename BOOST_MPL_AUX_SEQUENCE_TAG(Sequence) >
            ::template algorithm<
                  typename Sequence::type // apply0<T>::type
                , typename T::type // apply0<T>::type
>
    {
    };

the above code doesn't really work as expected - it actually _evaluates_ all
the metafunctions that were supposed to be put into the table, and stores
the invocation results instead.

Adding 'identity' there, besides being counter-intuitive, doesn't really
solve anything:

    // build a table of nullary metafunctions for later
    // (selective) invocation
    typedef push_back< f_table0,identity< my_f<arg1,arg2> > >::type
f_table1;
    typedef push_back< f_table1,identity< her_f<arg3> > >::type f_table2;
    ...

    // filter the table
    typedef copy_if<
          f_table10
        , push_back<_,_>
        , vector<>
        , is_needed
>::type f_result_table;

    // opps, all copied metafunctions got invoked along the way!

That's more, it's not only unary metafunctions that become affected by the
change; basically, "passing through" of anything expect "canonical" types
such as 'int_c' becomes a source of possible surprise and troubles:

    // doesn't work anymore!
    // typedef list<bool,char,short,int,long> types;

    // have to do this:
    typedef list<
          identity<bool>
        , identity<char>
        , identity<short>
        , identity<int>
        , identity<long>
> types; // ouch!

    // doesn't work either!
    typedef copy<
          filer_view< types, less< size_of<_>, size_of<int> >
        , push_back<_,_>
        , vector<>
>::type filtered_types;

Well, I hope you've got the picture.

>
> Paul Mensonides replied at the time that he found this a
> sensible solution to it. However, I haven't heard anything on this
> issue from others, such as Aleksey or Dave.

That's because I am yet to read those 200+ post-review MPL-related messages
that were posted during the 3-weeks period when I was out of country :).

Aleksey


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