Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2007-03-28 22:57:03


on Wed Mar 28 2007, Scott Meyers <usenet-AT-aristeia.com> wrote:

> JOAQUIN LOPEZ MU?Z wrote:
>> I think you want to write
>>
>> mpl::end<Constraints>::type
>
> Thanks for the suggestion, but it doesn't change the behavior of the program:
> the result of the insertion of a new element into the two-element set is a new
> set with only two elements (one of the original elements is lost). Please try
> it and see if you get the same thing I do.

Scott, I hate to say this, but I really think your first problem
demonstrated that mpl::set is too broken to use. I've asked Aleksey
to look at it, but he's not answering (maybe on vacation, I dunno).
I'd like to fix it myself as I was the one who came up with the basic
mechanisms for mpl::set, but unfortunately I can't tell what Aleksey
intended in his realization of those ideas so I'm not quite sure where
to start. You might see if you can use mpl::map to do the same things.

>> Well, excuse me if the following is obvious to you,
>> but ::type is to a metafunction what actual invocation is
>> to a run-time function. So, mpl::end<Constraints> refers
>> to the name of the entity, but does not actually compute
>> its "return value" unless you add the ::type suffix.
>
> I don't think this always holds.

Yeah, it always holds.

> For example, I don't need to add the ::type
> suffix when I make a typedef or when I do an assertion:
>
> typedef mpl::set<A, B> MySet;

There's no metafunction invocation there; you're just stating the name
ofa type (mpl::set<A,B>). mpl::set<...> may actually have a ::type
member (essentially a typedef for mpl::set<...> itself), but if so
it's just there as a convenience for use with constructs like eval_if.

    mpl::eval_if<
        predicate
      , some_metafunction_to_be_evaluated_if_pred_true<X>
      , mpl::set< ... >
>

it prevents you from having to write

    mpl::eval_if<
        predicate
      , some_metafunction_to_be_evaluated_if_pred_true<X>
      , mpl::identity<mpl::set< ... > >
        ^^^^^^^^^^^^^
>

> BOOST_MPL_ASSERT(( mpl::equal<mpl::set<A,B>, mpl::set<A,B> > ));

No explicit metafunction invocation there either... at least, not by
you. Again, you're just stating the name of a type:

  mpl::equal<mpl::set<A,B>, mpl::set<A,B> >

That type happens to be a nullary metafunction (any N-ary
metafunction, where N>0, with all its arguments filled in, is a
nullary metafunction: a class with a nested ::type). Actually the
extra set of parens is used by MPL to form a function type:

 int (mpl::equal<mpl::set<A,B>, mpl::set<A,B> >)

MPL strips the int(...) off to get your nullary metafunction, and then
it invokes that. Just as with the 2nd or 3rd argument to mpl::eval_if.

There's a class of metafunctions that usually don't need to be
explicitly invoked: those whose range of results is limited in certain
ways. In practice this turns out to mean metafunctions with
numeric/boolean results. For example, I could write a
metafunction to square a numeric value like this:

       template <class N>
       struct square
         : mpl::integral_c<
              N::value_type, N::type::value*N::type::value
>
       {};

In what sense is that a metafunction? Well, all MPL IntegralConstants
contain a nested ::type returning an equivalent IntegralConstant. So
integral_c looks like:

           template <class T, T x>
           struct integral_c
           {
              typedef T value_type;
              static T const value = x;
              typedef integral_c<T,x> type;
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           };

So integral_c<U,v> is a self-returning nullary metafunction (reach
inside to get its ::type, you get itself back). And square<X>
inherits most of those properties... enough to make it a conforming
IntegralConstant.

As a result, you could write

  mpl::add< square<mpl::int_<3> >::type, mpl::int_<1> >::type
                                 ^^^^^^
or

  mpl::add< square<mpl::int_<3> >, mpl::int_<1> >::type

and you'd get the same result. The same applies to all the Boost/TR1 type
traits with their boolean result types. That's why you can write either:

         mpl::if_<
                boost::is_pointer<X>::type
              , T // ^^^^^^
              , F
>::type

or

         mpl::if_<
                boost::is_pointer<X> // no ::type
              , T
              , F
>::type

and get the same result.

Incidentally, it's possible to construct a C++ template
metaprogramming system in which ::type (and typename!) is needed much
less frequently
(http://aspn.activestate.com/ASPN/Mail/Message/boost/2259201). I
think Vesa's approach had a lot of promise and I wish we'd had time to
pursue it.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com
Don't Miss BoostCon 2007! ==> http://www.boostcon.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