Boost logo

Boost :

From: Terje Slettebø (tslettebo_at_[hidden])
Date: 2002-09-19 08:16:46


>From: "Richard Crossley" <rdc_at_[hidden]>

> > I can certainly acknowledge that. In fact, I've suggested
> > that all metafunctions evaluate their own arguments.
> > [Addition: I see you suggest the same below here. :) ]
> > Similar to how apply_if evaluates the selected argument, as you say.
>
> How would this work with, for example the traits classes, assuming that
> for consistency they also evaluate their arguments? Would we now have to
> write remove_pointer<identity<char*> >::type or would the traits classes
> be specialised in some way for the built in types?

Yeah. That's one issue with this.

> Personally I 'get around the problem' with an additional template:
>
> template <typename T>
> struct eva; // evaluate arguments
>
> template <template <typename> class F,typename A0>
> struct eva<F<A0> > : F<typename A0::type>
> {};
> template <template <typename,typename> class F,typename A0,typename A1>
> struct eva<F<A0,A1> > : F<typename A0::type,typename A1::type>
> {};
>
> etc... So,
>
> apply_if<some_condition,
> some_type_1,
> plus<some_type_2::type,some_number >
> >::type;
>
> Becomes,
>
> apply_if<some_condition,
> some_type_1,
> eva<plus,some_type_2,some_number>
> >::type;
>
> Assuming some_number evaluates to its self, or if not,
>
> apply_if<some_condition,
> some_type_1,
> eva<plus,some_type_2,identity<some_number> >
>::type;

(By the way, I assume there should be a ",", rather than "<", after "plus"
above, so I've changed that in the quote above)

One advantage of this solution is that it works with MPL as it is.

However, I feel that having to use "eva" (or some other such metafunction)
to specifically evaluate arguments, may make the code more obscure. You now
have two ways of doing it, "::type" and "eva," and they both do the same
thing, except in different ways, and one may be needed instead of the other,
in some circumstances. If the metafunctions evaluates their own arguments,
you don't need to think about these issues. Furthermore, as Jaap correctly
said it, by having the metafunctions evaluate their own parameters, you have
a kind of lazy evaluation, where you ensure that it doesn't instantiate more
than it needs.

You can of course standardise on one way of doing it, such as using "::type"
where possible, or "eva" where not possible, or consistently use "eva".

Consider the "factorial" metafunction, again:

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

I had first planned to look at a comparison with using "eva," but I realized
that I had a hard time finding out how I could write the above, using "eva,"
since the call to factorial is used as a parameter to "mul," and unlike
apply_if, that doesn't evaluate its arguments, which is what makes your
first "eva" example work.

Could you have shown how I could rewrite the above, using "eva"?

Unless there's something obvious I've missed, I think the fact that it's
hard to write one of the simplest metafunctions, factorial, using "eva,"
could mean that it could be hard to use this solution.

Regarding your first comment, about the type traits, that's a legitimate
issue. Perhaps an exception could be made for them. You might also, as you
say, specialise for built-in types. This is an open issue. However, I feel
the advantage in having metafunctions evaluate their own arguments is so
great, that it should be possible to find a solution to this. Besides not
having to split the code up, I feel it becomes cleaner, when you don't have
to have all those "::type" in the code. After all, run-time programming
doesn't use named return values, either (similar to the "::type"). Consider
the following line (ignoring for a moment that the "::type" solution doesn't
work, but you would have to have something there, anyway, such as "eva"):

mul<Value,factorial<prior<Value> > >

compared to:

mul<Value,factorial<prior<Value>::type>::type>

"factorial" is a simple metafunction. The effect of this could be much
bigger for more complicated functions.

Regards,

Terje


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