|
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