Reputation: 2051
I'm writing some code to format a hex number, and currently it accepts an unsigned 64-bit value. This was all fine and dandy until I realized that it formats a 32-bit signed integer with too much sign extension, that is, "-1" becomes "ffffffffffffffff".
I could solve this by using numeric_limits<T>::digits:
template<class Int>
void Format(Int v) {
switch(std::numeric_limits<Int>::digits) {
case 7:
case 8:
CallFormatRoutine(0xFF & v);
break;
case 15:
case 16:
CallFormatRoutine(0xFFFF & v);
break;
case 31:
case 32:
CallFormatRoutine(0xFFFFFFFF & v);
break;
case 63:
case 64:
CallFormatRoutine(v);
break;
}
}
But I was wondering if there was some better (shorter) way. Specifically, a better way that works with C++03 and C++11. Ideally there would be some routine that would return the unsigned version of any integer you give it, but I haven't been able to find that in the standard. If I could use C++11, I could write this, albeit perhaps with a cast to avoid a warning about signed/unsigned conversion:
template<class Int>
void Format(Int v) {
std::make_unsigned<Int>::type unsigned_v = v;
CallFormatRoutine(unsigned_v);
}
Is there anything nice and short to do this, that also works with C++03?
Upvotes: 1
Views: 607
Reputation: 2051
FWIW, I ended up going with just using sizeof instead of std::numeric_limits, and using static_cast rather than 0xFFFFFFFF:
template<class Int>
void Format(Int v) {
static_assert(sizeof(v) == 1 || sizeof(v) == 2 || sizeof(v) == 4 || sizeof(v) == 8,
"Unknown integer type");
CallFormatRoutine( sizeof(v) == 1 ? static_cast<uint8>(v)
: sizeof(v) == 2 ? static_cast<uint16>(v)
: sizeof(v) == 4 ? static_cast<uint32>(v)
: static_cast<uint64>(v);
}
(I tried to post this a comment to my question but couldn't get multiline code to format correctly.)
Upvotes: 1
Reputation: 109189
std::make_unsigned
is the nicest and shortest way to accomplish what you want, but you have some errors in your example. It should look something like this:
template<class Int>
Format(Int v) {
auto unsigned_v = static_cast<typename std::make_unsigned<Int>::type>(v);
CallFormatRoutine(unsigned_v);
}
And with a C++14 compiler, the relevant line can be shortened to
auto unsigned_v = static_cast<std::make_unsigned_t<Int>>(v);
For C++03 compilers, there's boost::make_unsigned
, or you could implement it yourself, but that's a little tedious because you need to handle all the built-in types.
template<typename T>
struct make_unsigned
{ typedef T type; };
template<>
struct make_unsigned<char>
{ typedef unsigned char type; };
template<>
struct make_unsigned<signed char>
{ typedef unsigned char type; };
Similarly, add specializations for short
, int
and the remaining integral types.
Upvotes: 3