Portably Selecting an Integer Type of a Specific Size?

S

Simon G Best

Hello!

The C++ standard library provides facilities for finding out the sizes
(and other such stuff) of numeric types :):std::numeric_limits<>, for
example).

What I would like to do is to select a type on the basis of its traits.
More specifically, I would like to select an unsigned integer type on
the basis of the number of bits I need it to have. (This is because
there are particularly efficient ways of implementing the Advanced
Encryption Standard (AES) block ciphers using 32 bit words.)

I *could* do some template metaprogramming to find a suitable type - but
I'd rather not. It seems rather klunky, and is the sort of thing that
makes me think, 'There must be a better way!'

I *could*, I suppose, use the macros in the relevant headers inherited
from C - but, again, I'd rather not. Macros feel kludgy for this sort
of thing, and I much prefer doing things within the language itself.

So, what I was wondering is if there is a good, general way of selecting
types on the basis of desired traits, and, if so, what that general way
is. (I won't be surprised if it involves a little bit of template
metaprogramming, but I'd prefer it to be as little as possible.)

Or, if there isn't a good, general way for doing that, if there's a good
way of doing the more specific thing of selecting an integer type on the
basis of the desired size.

At the moment, the best idea I've got (for the more specific thing I
want to do) is to combine templates with constant expressions, something
like this:-

#include <limits>

namespace {

template< class T >
Y f_implementation( X x ) {

/* f implemented in terms of T. */
}

class last_resort;
/* A class that implements 32 bit unsigned integers
if none of the built in types are 32 bits.
*/

};

Y f( X x ) {

if( ::std::numeric_limits<unsigned int>::digits == 32 )
return f_implementation<unsigned int>(x);

if( ::std::numeric_limits<unsigned int>::digits > 32 ) {

if( ::std::numeric_limits<unsigned short>::digits == 32 )
return f_implementation<unsigned short>(x);

if( ::std::numeric_limits<unsigned char>::digits == 32 )
return f_implementation<unsigned char>(x);
}

if( ::std::numeric_limits<unsigned long>::digits == 32 )
return f_implementation<unsigned long>(x);

return f_implementation<last_resort>(x);
}

That's obviously not a truly generic way of doing it, as it explicitly
goes through some explicit possibilities. A truly generic way would be
a lot nicer! (But I suspect there isn't one, as two different types
might have an identical trait.)

Any thoughts?

:)

Simon
 
S

Shane Beasley

Simon G Best said:
The C++ standard library provides facilities for finding out the sizes
(and other such stuff) of numeric types :):std::numeric_limits<>, for
example).

What I would like to do is to select a type on the basis of its traits.
More specifically, I would like to select an unsigned integer type on
the basis of the number of bits I need it to have. (This is because
there are particularly efficient ways of implementing the Advanced
Encryption Standard (AES) block ciphers using 32 bit words.)

I *could* do some template metaprogramming to find a suitable type - but
I'd rather not. It seems rather klunky, and is the sort of thing that
makes me think, 'There must be a better way!'

Actually, templates are often the best way, or even the only way. If
you want it done at compile time, you can do it before C++ gets to it
(via hardcoding values yourself or having a script do it for you); you
can use C++ preprocessing macros; or you can use templates.
Furthermore...
I *could*, I suppose, use the macros in the relevant headers inherited
from C - but, again, I'd rather not. Macros feel kludgy for this sort
of thing, and I much prefer doing things within the language itself.

....macros alone can't do this. While CHAR_BIT and the min/max values
of each type are available at the preprocessor level, sizeof is not,
nor (afaik) is an O(1) algorithm for calculating base-2 logarithms
(which you could theoretically use, along with the maximum unsigned
value of a type, to determine the number of bits for that type) -- it
has to be O(1) because macros can't loop or recurse.

NB: While macros are evil[1], they do have their uses. In fact, they
are of some use here, depending on how little metaprogramming you'd
like to do.
So, what I was wondering is if there is a good, general way of selecting
types on the basis of desired traits, and, if so, what that general way
is. (I won't be surprised if it involves a little bit of template
metaprogramming, but I'd prefer it to be as little as possible.)

The *general* good way is heavy on metaprogramming. It relies upon a
recursive template algorithm over a typelist which resembles
std::find_if over std::list. However, there may be simpler ways which
aren't necessarily general.
Or, if there isn't a good, general way for doing that, if there's a good
way of doing the more specific thing of selecting an integer type on the
basis of the desired size.

I came up with two ways, one generic but heavy on templates and one
using the preprocessor but particular to the problem at hand:

<http://acm.eecs.uic.edu/~sbeasley/code/metasize/>

The metaprogramming example uses the find_if algorithm described
above. The current implementation uses partial template specialization
which chokes versions of Visual C++ prior to .NET 2003, but since I
don't have immediate access to them, I'll leave getting them to play
nice together as an exercise for the reader.

The other example defines a template bits<N>, then specializes it for
each of the monotonically increasing sizes of (char, short, int,
long). The trick is that the preprocessor does *not* include
specialization for a type's size unless it is bigger than the previous
type:

#if USHRT_MAX > UCHAR_MAX
template <>
struct bits<std::numeric_limits<unsigned short>::digits> {
typedef signed short signed_type;
typedef unsigned short unsigned_type;
};
#endif

Here, if short is bigger than char, then both will have
specializations; otherwise, only char will. The preprocessor is used
exclusively for conditional compilation, not macro definition, and
there's nothing evil about that. :)
At the moment, the best idea I've got (for the more specific thing I
want to do) is to combine templates with constant expressions, something
like this:-

#include <limits>

namespace {

template< class T >
Y f_implementation( X x ) {

/* f implemented in terms of T. */
}

class last_resort;
/* A class that implements 32 bit unsigned integers
if none of the built in types are 32 bits.
*/

};

Y f( X x ) {

if( ::std::numeric_limits<unsigned int>::digits == 32 )
return f_implementation<unsigned int>(x);

if( ::std::numeric_limits<unsigned int>::digits > 32 ) {

if( ::std::numeric_limits<unsigned short>::digits == 32 )
return f_implementation<unsigned short>(x);

if( ::std::numeric_limits<unsigned char>::digits == 32 )
return f_implementation<unsigned char>(x);
}

if( ::std::numeric_limits<unsigned long>::digits == 32 )
return f_implementation<unsigned long>(x);

return f_implementation<last_resort>(x);
}

A smart compiler may recognize std::numeric_limits<T>::digits as a
compile-time constant, determine at compile time which branch will be
taken, and optimize the rest away. However, the code *looks* like it
makes this decision at run time, which may rub many programmers --
myself included -- the wrong way.
That's obviously not a truly generic way of doing it, as it explicitly
goes through some explicit possibilities. A truly generic way would be
a lot nicer! (But I suspect there isn't one, as two different types
might have an identical trait.)

You can't choose from a set of possibilities without going through
them.

Anyway, good luck!

- Shane
 
S

Simon G Best

Shane Beasley wrote:
[...]
NB: While macros are evil[1], they do have their uses. In fact, they
are of some use here, depending on how little metaprogramming you'd
like to do.

Hmmm, yes, I had rather hastily rejected conditional compilation out of
my keenness to avoid macros...
The *general* good way is heavy on metaprogramming. It relies upon a
recursive template algorithm over a typelist which resembles
std::find_if over std::list. However, there may be simpler ways which
aren't necessarily general.

Yeah, that was the sort of thing that got me thinking, 'There must be a
better way.'
I came up with two ways, one generic but heavy on templates and one
using the preprocessor but particular to the problem at hand:

<http://acm.eecs.uic.edu/~sbeasley/code/metasize/>

Thanks :)

[...]
A smart compiler may recognize std::numeric_limits<T>::digits as a
compile-time constant, determine at compile time which branch will be
taken, and optimize the rest away. However, the code *looks* like it
makes this decision at run time, which may rub many programmers --
myself included -- the wrong way.

Hmmm, yes, that's a good point. Stuff that's not notionally done at
runtime ought not to be written in a notionally runtime way. So, I'll
not do it that way now :)
You can't choose from a set of possibilities without going through
them.

Anyway, good luck!

- Shane

Thanks for your thoughts and advice! :)

I think I'll do it with conditional compilation (with them macros), and
see how it goes...

:)

Simon
 
S

Simon G Best

Shane said:
(I could swear I posted something about this before, but since Google
Groups seems to disagree, I'll "repost" it. Sorry if it's a
duplicate.)

Well, it doesn't seem to have come up on the news server I use either, so...
(e-mail address removed) (Shane Beasley) wrote in message news:<[email protected]>... [...]
...macros alone can't do this.


Oops, I lied. They can. I know, because I made it happen. :)
:)

[...]
However, if you're just shooting for 8-, 16-, 32-, and 64-bit types,
you can try right-shifting UINT_MAX and friends by the desired bit
lengths +/- 1 to see when they go to zero. It even works in C. :)

Yeah, that seems reasonable...

[...]
I added the third way, using macros. Of course, if you find it
"kludgy", feel free to use something else.

Enjoy!

- Shane

Thanks again! :)

Simon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top