How does one generically convert signed and unsigned integers into each other without having to specify the specific width?
For example:
uint8_t <-> int8_t
uint16_t <-> int16_t
uint32_t <-> int32_t
uint64_t <-> int64_t
It would be nice to write:
uint32_t x;
int32_t y;
static_cast<signed>(x)
static_cast<unsigned>(y)
However, I suspect that doesn't do what I want.
I think I can achieve a similar effect using this method but I would think doing it for the same data width might have some syntactic sugar.
Conditional template type math
CodePudding user response:
However, I suspect that doesn't do what I want.
Indeed, your suspicion is valid: the expression, static_cast<signed>(x)
, is exactly equivalent to static_cast<signed int>(x)
, so the cast will always be to an object of the 'default' size of an int
on the given platform (and, similarly, unsigned
is equivalent to unsigned int
).
However, with a little bit of work, you can create 'generic' cast template functions that use the std::make_signed
and std::make_unsigned
type-trait structures.
Here's a possible implementation of such functions, with a brief test program that shows some deduced result types for different input type widths:
#include <type_traits> // For make_signed and make_unsigned
template<typename T>
static inline auto unsigned_cast(T s)
{
return static_cast<std::make_unsigned<T>::type>(s);
}
template<typename T>
static inline auto signed_cast(T s)
{
return static_cast<std::make_signed<T>::type>(s);
}
#include <typeinfo> // For "typeid"
#include <iostream>
int main()
{
int16_t s16 = 42;
auto u16 = unsigned_cast(s16);
std::cout << "Type of unsigned cast is: " << typeid(u16).name() << "\n";
uint64_t u64 = 39uLL;
auto s64 = signed_cast(u64);
std::cout << "Type of signed cast is: " << typeid(s64).name() << "\n";
return 0;
}