I want to take a stack variable and reinterpret cast
it into an unsigned integer type of the same size in bytes. For example, I might want to take double<
If you don't want to have undefined behavior due to violating the aliasing restrictions (C++11 3.10/10) then you need to access the object representations as characters:
template <typename T>
int_type<T> caster(const T& value) {
int_type<T> v;
static_assert(sizeof(value) == sizeof(v), "");
std::copy_n(reinterpret_cast<const char*>(&value),
sizeof(T),
reinterpret_cast<char*>(&v));
return v;
}
High quality compilers will optimize the copy away. E.g., this program:
int main() {
return caster(3.14f);
}
effectively optimizes to return 1078523331; on Intel processors.
Between std::conditional_t
and std::enable_if_t
I believe that you can compress all your helper
and int_type
definitions into a self-sufficient caster
function:
template <typename T>
auto caster(T value){return reinterpret_cast<std::conditional_t<sizeof(T) == sizeof(uint8_t),
uint8_t,
conditional_t<sizeof(T) == sizeof(uint16_t),
uint16_t,
conditional_t<sizeof(T) == sizeof(uint32_t),
uint32_t,
enable_if_t<sizeof(T) == sizeof(uint64_t),
uint64_t>>>>&>(value);}
I've validated that this works on both gcc 4.9.2 and Visual Studio 2015, if you only have C++11 support though you can still get this into a self-sufficient caster
function:
template <typename T>
typename std::conditional<sizeof(T) == sizeof(uint8_t),
uint8_t,
typename conditional<sizeof(T) == sizeof(uint16_t),
uint16_t,
typename conditional<sizeof(T) == sizeof(uint32_t),
uint32_t,
typename enable_if<sizeof(T) == sizeof(uint64_t),
uint64_t>::type>::type>::type>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
This will pick the uint*
that has the same sizeof
as the type you pass to it and use that.
I have an explaination of std::enable_if
over here that may be helpful to you.
Obviously this is just useful on types that are 8, 16, 32, or 64-bits in size, but if you feel like expanding it to handle other stuff, just add another conditional_t
!
If you are only ever going to pass in 8, 16, 32, or 64-bit types you can get away with less protection in your template:
template <typename T>
auto caster(T value){return reinterpret_cast<std::tuple_element_t<size_t(log2(sizeof(T))), std::tuple<uint8_t,
uint16_t,
uint32_t,
uint64_t>>&>(value);}
This works for C++14, the C++11 equivalent is:
template <typename T>
typename std::tuple_element<size_t(log2(sizeof(T))), std::tuple<uint8_t,
uint16_t,
uint32_t,
uint64_t>>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
This is less forgiving than the conditional_t
/enable_if_t
template because of how I am indexing the std::tupple
. size_t
is an integral type so any type of any size less than 128-bits will cast to a valid std::tuple
index. So for example a struct
that was 3-bits in size would be cast to a uint16_t
, while the desired result would probably have been for it to fail to compile.