jorgbrown
jorgbrown

Reputation: 2051

Shortest/cleanest way to turn an integer of unknown size into its unsigned form?

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

Answers (2)

jorgbrown
jorgbrown

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

Praetorian
Praetorian

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

Related Questions