Here's a reasonably
standard and
portable way to define
int types with an (exact) desired
number of
bits, in
C++. It uses
typelists, so you'd best read about those first!
We'll be using a standard C macro, CHAR_BITS, defined in <climits>. And, of course, the standard typelist definitions (a real implementation would just #include Loki's definitions).
#include <climits>
// typelists (from Loki's Typelist.h)
class NullType {};
template <typename H, class T>
struct Typelist {
typedef H Head;
typedef T Tail;
};
// #define's for TYPELIST_1, TYPELIST_2, ...; see typelist.
We also need a
compile time means for
conditional type selection. Loki's
Select<flag, IfTrue, IfFalse>::Result fulfills this need:
// Select (from Loki's TypeManip.h)
template<bool flag, typename T, typename U>
struct Select {
// used when flag is `true'
typedef T Result;
};
template<typename T, typename U>
struct Select<false, T, U> {
// specialise when flag is `false'
typedef U Result;
};
That's all we need from Loki (see Alexandrescu's excellent book
Modern C++ Design for the real story; this is just to
whet your appetite!).
Now let's define our type. First, we declare a type NoTypeFound, but (unlike NullType above!) we don't define it. The SelectInt template will instantiate using this type only if no appropriate type was found. And thanks to the C++ template rules, it's only if we attempt to instantiate using NoTypeFound that an error will be flagged.
class NoTypeFound;
// only declared -- use will trigger an error!
SelectInt<TL,bits> searches the typelist TL for a type that is exactly bits bits large; SelectInt<TL,bits>::type will be such a type (or NoTypeFound if none such exists).
// Select bits-bits integer from typelist TL
template<class TL, int bits>
struct SelectInt;
template<int bits>
struct SelectInt<NullType, bits> {
// Empty list -- no type was found!
typedef NoTypeFound type;
};
template<class Head, class Tail, int bits>
struct SelectInt<Typelist<Head,Tail>, bits> {
typedef typename
Select< sizeof(Head)*CHAR_BIT == bits,
Head,
typename SelectInt<Tail, bits>::type
>::Result
type;
};
Whew! Let's take it one step at a time.
TL could be
NullType, in which case we generate a type which will flag an error if used (an
empty list certainly doesn't contain our desired type!). Or it could be
Typelist<Head,Tail>. If so, we check (using
Select) if
Head has precisely
bits bits. If it does, we pick it. Otherwise, we
recurse on
Tail.
Whatever the result, we typedef it into type.
Now we just have to pass an appropriate typelist to SelectInt:
template<int bits>
class Int {
// List of types to check
typedef
TYPELIST_5(int, signed char, short, long, long long)
SignedInts;
public:
typedef typename SelectInt<SignedInts,bits>::type type;
};
That's it! Note, however, that the unlovely
long long isn't really standard. So if we include it we're non-standard, and if we don't we probably won't find a
Int<64> on most platforms! Still, it beats most alternatives...
To test it, copy all code to a file, then write code such as:
int main()
{
// Int<17>::type x; // Compile-time error on any
// reasonable platform
Int<16>::type i2;
Int<32>::type i4;
Int<64>::type i8;
// do stuff, write sizeof's, ...
}