Boost logo

Boost :

From: Gennaro Prota (gennaro_prota_at_[hidden])
Date: 2003-02-02 07:47:44


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 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?
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 = ...;
   };

Since log2<0> gives an error anyway, this would change nothing for the
normal usage of computing a logarithm (i.e.: he should specify the
argument anyway, as it is now). But, with that default, you could
have:

  template <>
  struct argument_type< static_log2<> > {
     typedef static_log2_argument_type type;
  };

  // usage:
  argument_type < static_log2<> > :: type

However I don't like too much the idea to have a default argument
(zero) which is a non-sense for the main usage of the template
(calculating an algorithm) and whose only purpose is to use a simpler
template-id in conjunction with argument_type<>. Also this is quite an
ad-hoc approach, not suitable for other "unary compile-time-function
templates" for which no "unusable default" exists. In short: I don't
know what to do :-) Maybe we should simply have the convention that
such templates provide typedefs with standard names, like
argument_type and result_type?

Thoughts?

Genny.


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