views:

91

answers:

4

Often I have some compile-time constant number that is also the upper limit of possible values assumed by the variables. And thus I'm interested in choosing the smallest type that can accomodate those values. For example I may know that variables will fit into <-30 000, 30 000> range, so when looking for a suitable type I would start with signed short int. But since I'm switching between platforms and compilers I would like a compile-time assert checking whether the constant upper values really fit within those type. BOOST_STATIC_ASSERT( sizeof(T) >= required_number_of_bytes_for_number ) works fine but the problem is: How to automatically determine the number of bytes required for storing a given compile-time constant, signed or unsigned? I guess a C macro could do this job? Could anyone write it for me? I might use std::numeric_limits::max() and min() instead of computing the bytes but then I would have to switch to run-time assert :(

A: 

How about you avoid the problem:

BOOST_STATIC_ASSERT((1LL << (8*sizeof(T))) >= number);
Oli Charlesworth
Can someone explain how this works?
@user467799: For a start, I forgot to multiply by 8. But even then, this isn't 100% portable, so probably best to go with a different suggestion.
Oli Charlesworth
+4  A: 

Now that this is tagged with c++, I suggest using Boost.Integer for appropriate type selection. boost::int_max_value_t< MyConstant >::least would give the type you are looking for.

usta
Yes, that is great. Link to docs: http://www.boost.org/doc/libs/1_44_0/libs/integer/doc/html/boost_integer/integer.html
A: 

You may use the following code. It works only for positive 8/16/32/64bit integers. But you may do the appropriate changes for negative values as well.

template <typename T, T x> class TypeFor
{
    template <T x>
    struct BitsRequired {
        static const size_t Value = 1 + BitsRequired<x/2>::Value;
    };
    template <>
    struct BitsRequired<0> {
        static const size_t Value = 0;
    };


    static const size_t Bits = BitsRequired<x>::Value;
    static const size_t Bytes = (Bits + 7) / 8;

    static const size_t Complexity = 1 + BitsRequired<Bytes-1>::Value;

    template <size_t c> struct Internal {
    };

    template <> struct Internal<1> {
        typedef UCHAR Type;
    };
    template <> struct Internal<2> {
        typedef USHORT Type;
    };
    template <> struct Internal<3> {
        typedef ULONG Type;
    };
    template <> struct Internal<4> {
        typedef ULONGLONG Type;
    };

public:

    typedef typename Internal<Complexity>::Type Type;

};


TypeFor<UINT, 117>::Type x;

P.S. this compiles under MSVC. Probably some adjustment should be done to adopt it for gcc/mingw/etc.

valdo
A: 

How about BOOST_STATIC_ASSERT(int(60000)==60000) ? This will test whether 60000 fits in an int. If int is 16 bits, int(60000) is 27232. For the comparison, this will then be zero-extended back to a 32 bits long, and fail reliably.

MSalters