Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-02-02 08:35:56

Gennaro Prota <gennaro_prota_at_[hidden]> writes:

> This is a simple question; suppose you have a template like this:
> template < unsigned long x >
> struct static_log2
> {
> BOOST_STATIC_CONSTANT( int, value = ...);
> };
> and that the implementation you use is absolutely general as far as
> the argument type is concerned; in other words: if C++0x will make
> unsigned long long available you want to replace unsigned long with
> that wider type. Specifically, this is the case for the excellent
> static_log2 implementation suggested by Vesa Karvonen a while ago on
> this list. A first, obvious, way to do this is to typedef the argument
> type:
> namespace boost {
> typedef unsigned long static_log2_argument_type;
> template < static_log2_argument_type x>
> struct static_log2 {
> ...
> }
> }

*This* is precisely why you metafunctions should operate on types, not
numbers. Types are the only fully polymorphic metadata because they
can wrap anything else (well, as soon as we get template typedefs they
will be able to wrap anything else, but they're close enough now).
That's why MPL has integral_c<T, value>.

> This of course works, but the typedef name is quite "log specific";
> certainly that's worse than having a generic name available at class
> scope:
> template <...>
> struct static_log2 {
> typedef ... argument_type; // (*)
> };
> As Terje Slettebø noticed in private mail, this is a general problem
> for any 'compile-time function'.


> A similar issue, for the run-time counter-part, is solved by
> std::unary_function and std::binary_function. What should we do for
> the compile-time case?

I don't think the analogy is a good one. unary_ and binary_function
just supply the typedefs for /specific/ function argument types.

The compile-time case should be handled by passing types instead of
numbers. If you need the other interface for convenience or legacy
reasons you can always write static_log2_c<unsigned long>.

Note that using types can allow you to compute a static log2 of
extended long values even if the C++ compiler doesn't have them built
in. One just needs an appropriate wrapper type such as:

     template <unsigned long hi, unsigned long lo>
     struct long_long
        typedef long_long type;
        unsigned long hi_value;
        unsigned long lo_value;

and specializations of all of the arithmetic primitive metafunctions
you'll need to use to operate on it.

> Note that to use argument_type in the above (*) you have to specify
> an argument for static_log2, despite the fact that actually the
> typedef doesn't depend on the value of which you compute the
> logarithm. Even if you introduce, say, an argument_type<> template,
> like this:
> // specialize this as needed
> template <typename T> struct argument_type;
> template <static_log2_argument_type x>
> struct argument_type< log<x> > {
> typedef static_log2_argument_type type;
> };
> you still don't solve the problem of the dummy argument: e.g.
> argument_type< log<1> > :: type; // why 1?
> Unfortunately you can't default x to zero in the argument_type
> specialization above. In fact, in this specific case, you could resort
> to:
> template <static_log2_argument_type x = 0>
> struct log {
> static const int value = ...;
> };

You've completely lost me here. I tried to understand it three times,
and gave up.

                       David Abrahams
   dave_at_[hidden] *
Boost support, enhancements, training, and commercial distribution

Boost list run by bdawes at, gregod at, cpdaniel at, john at