Я пишу некоторый код для форматирования шестнадцатеричного числа, и в настоящее время он принимает 64-разрядное значение без знака. Это было все прекрасно, пока я не понял, что он форматирует 32-разрядное целое число со знаком со слишком большим расширением знака, то есть «-1» становится «ffffffffffffffff».
Я мог бы решить это с помощью numeric_limits<T> :: цифры:
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;
}
}
Но мне было интересно, есть ли какой-нибудь лучший (более короткий) путь. В частности, лучший способ, который работает с C ++ 03 и C ++ 11. В идеале должна быть какая-то подпрограмма, которая будет возвращать беззнаковую версию любого целого числа, которое вы ей дадите, но я не смог найти это в стандарте. Если бы я мог использовать C ++ 11, я мог бы написать это, хотя, возможно, с приведением, чтобы избежать предупреждения о преобразовании со знаком / без знака:
template<class Int>
void Format(Int v) {
std::make_unsigned<Int>::type unsigned_v = v;
CallFormatRoutine(unsigned_v);
}
Есть ли что-нибудь хорошее и короткое, чтобы сделать это, что также работает с C ++ 03?
std::make_unsigned
это самый хороший и самый короткий способ выполнить то, что вы хотите, но у вас есть некоторые ошибки в вашем примере. Это должно выглядеть примерно так:
template<class Int>
Format(Int v) {
auto unsigned_v = static_cast<typename std::make_unsigned<Int>::type>(v);
CallFormatRoutine(unsigned_v);
}
А с помощью компилятора C ++ 14 соответствующая строка может быть сокращена до
auto unsigned_v = static_cast<std::make_unsigned_t<Int>>(v);
Для компиляторов C ++ 03 есть boost::make_unsigned
, или вы могли бы реализовать это самостоятельно, но это немного утомительно, потому что вам нужно обрабатывать все встроенные типы.
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; };
Аналогично, добавьте специализации для short
, int
и остальные целочисленные типы.
FWIW, я закончил с использованием sizeof вместо std :: numeric_limits и использованием static_cast вместо 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);
}
(Я пытался опубликовать комментарий к моему вопросу, но не смог получить многострочный код для правильного форматирования.)