Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2003-03-22 22:45:18


"cj2048" <cj2048_at_[hidden]> writes:

> Hello everyone,
>
> I've only recently taken my first small steps into the world of
> metaprogramming, and now I've hit a snag. I'm trying to create a
> metafunction that returns the smallest unsigned integral type with at
> least the specified number of bits and, if no such type exists,
> reports an error.
>
> For example, ceil_uint<24> (named so for lack of a better name)
> returns the first member of the list consisting of
>
> unsigned char
> short unsigned int
> unsigned int
> long unsigned int
>
> whose number of bits is greater than or equal to 24.

The easy answer is http://www.boost.org/libs/integer/integer.htm
But assuming you want to do this yourself...

> Now, ceil_uint<N> seems to work fine for 0 <= N <= 32, but with N
> outside this range, g++ (3.2) gives me a compilation error:
>
> [cej_at_kirk aux]$ g++ -I/usr/include/boost test.cpp -o test
> test.cpp: In function `int main()':
> test.cpp:48: conversion from `int' to non-scalar type
> `boost::mpl::void_'
> requested
> test.cpp:49: no match for `-- boost::mpl::void_&' operator
> test.cpp:50: no match for `boost::mpl::void_& == unsigned char'
> operator
> test.cpp:52: no match for `boost::mpl::void_& == short unsigned int'
> operator
> test.cpp:54: no match for `boost::mpl::void_& == unsigned int'
> operator
>
> I guess that a compilation error is desirable in these cases, but is
> there some MPL-blessed way to make the error message(s) more
> informative (i.e., tell the user that N is outside the valid range)?

Not yet. BOOST_STATIC_ASSERT() is a pretty good solution, but it
doesn't put any nice text like "out of range" in your error message.

> I'd greatly appreciate any and all suggestions for improving the
> metafunction. I'd also like to know the generally recommended way to
> separate the "invocation" of the metafunction from its definition.

Use separate headers? Sounds too obvious; you must be after
something else...

> I intend to invoke the metafunction in a header file like this:
>
> namespace X {
> /* a definition of ceil_uint here would clutter
> the interface of class Y */
> template<int N>
> class Y {
> /* ... */
> typename ceil_uint<N>::type data; // invocation
> };
> }
>
> Having the definition and the invocation in the same header file seems
> to clutter the interface of the class.

> /* test program */
>
> #include "boost/mpl/vector.hpp"
> #include "boost/mpl/find_if.hpp"
> #include "boost/mpl/transform.hpp"
> #include "boost/mpl/multiplies.hpp"
> #include "boost/mpl/sizeof.hpp"
> #include "boost/mpl/greater_equal.hpp"
> #include "boost/mpl/int.hpp"
> #include "boost/mpl/at.hpp"
> #include "boost/mpl/distance.hpp"
>
> #include <limits>
> #include <iostream>
>
> namespace mpl = boost::mpl;
> using namespace mpl::placeholders;
>

First point: MPL metafunctions operate on types, not numbers. To stay
consistent, pass integral type wrappers like int_<N>.

> template<int N>
> struct ceil_uint {
> typedef mpl::vector<
> unsigned char, short unsigned int,
> unsigned int, long unsigned int
> > types;
>
> typedef typename mpl::transform<
> types,
> mpl::multiplies<
> mpl::sizeof_<_>,
> mpl::int_<std::numeric_limits<unsigned char>::digits>
> >
> >::type result;

Second point: I don't think this is consistent with the standard.
Does the standard guarantee that an unsigned integral type of size M
has M*CHAR_BIT usable bits?

> typedef typename mpl::find_if<
> result,
> mpl::greater_equal< _1, mpl::int_<N> >
> >::type iter;

You could

    BOOST_STATIC_ASSERT((!is_same<iter, typename mpl::end<result>::type>::value))

right here.

> typedef typename mpl::at<
> types,
> typename mpl::distance<
> typename mpl::begin<result>::type,
> iter
> >::type
> >::type type;
> };

Hmm, well it's a very inefficent way to get where you're going because
you have to construct and traverse several type sequences. How about:

    template <class T>
    struct digits
       : mpl::int_<std::numeric_limits<T>::digits>
    {};

    template <class N>
    struct ceil_uint
      : typename mpl::find_if<
            mpl::vector<
              unsigned char, short unsigned int,
              unsigned int, long unsigned int
>
          , mpl::greater_equal<digits<_1>, N>
>::type
    {};

??

If you want to check for errors, you can use mpl::aux::has_type to
check whether ceil_uint is valid:

      BOOST_STATIC_ASSERT(mpl::aux::has_type<ceil_uint<T> >::type::value);

and if you want a nice error message, do something like:

    number_of_bits_out_of_range(mpl::true_);

    ...

    enum { whatever = sizeof(
            number_of_bits_out_of_range(mpl::aux::has_type<ceil_uint<T> >::type)
           ) };

HTH,

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.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