In the following question:
What's a proper way of type-punning a float to an int and vice-versa?, the conclusion is that the way to construct doubles from integer bits and vise versa is via memcpy
.
That's fine, and the pseudo_cast
conversion method found there is:
template <typename T, typename U>
inline T pseudo_cast(const U &x)
{
static_assert(sizeof(T) == sizeof(U));
T to;
std::memcpy(&to, &x, sizeof(T));
return to;
}
and I would use it like this:
int main(){
static_assert(std::numeric_limits<double>::is_iec559);
static_assert(sizeof(double)==sizeof(std::uint64_t));
std::uint64_t someMem = 4614253070214989087ULL;
std::cout << pseudo_cast<double>(someMem) << std::endl; // 3.14
}
My interpretation from just reading the standard and cppreference is/was that is should also be possible to use memmove
to change the effective type in-place, like this:
template <typename T, typename U>
inline T& pseudo_cast_inplace(U& x)
{
static_assert(sizeof(T) == sizeof(U));
T* toP = reinterpret_cast<T*>(&x);
std::memmove(toP, &x, sizeof(T));
return *toP;
}
template <typename T, typename U>
inline T pseudo_cast2(U& x)
{
return pseudo_cast_inplace<T>(x); // return by value
}
The reinterpret cast in itself is legal for any pointer (as long as cv is not violated, item 5 at cppreference/reinterpret_cast). Dereferencing however requires memcpy
or memmove
(§6.9.2), and T and U must be trivially copyable.
Is this legal? It compiles and does the right thing with gcc and clang.
memmove
source and destinations are explicitly allowed to overlap, according
to cppreference std::memmove and memmove,
The objects may overlap: copying takes place as if the characters were copied to a temporary character array and then the characters were copied from the array to dest.
Edit: originally the question had a trivial error (causing segfault) spotted by @hvd. Thank you! The question remains the same, is this legal?
x
and the return value ofpseudo_cast_inplace
point to the same memory location but have different types. – Mayamayakovskistd::memmove(&toP, ...)
--toP
is a pointer already, you don't want a pointer to a pointer at this point. – Pyjamaspseudo_cast
just becomesvmovsd xmm1,qword ptr [rsp+50h]
.memcpy
is well known and gets special treatment from many compilers. – Puetteffective type
ofmemmove
input and output in this case is the sameU
type? – Housewiferystd::memmove(toP, &x, sizeof(T));
makes no sense after that line:T* toP = reinterpret_cast<T*>(&x); std::memmove(toP, &x, sizeof(T));
. Don't write such code! – AnisetteDouble.longBitsToDouble(0x3FFL << 52 | x >>> 12) - 1.0
and the question I linked to shows how to do it portably (under implementation specified constraints, not undefined), given sufficient asserts, eg ::is_iec559, sizeof, ... – Elenestd::vector
resize_uninitialized(size)
for trivial types before filling up the (gigabytes) of data.reserve
+push_back
works sometimes, but not for example when splitting up processing of the vector in chunks. – Elenedouble from_bit_pattern(uint64_t bits)
. – Anisettelong
values, and another (likewise unalterable) that reads an array of 64-bitlong long
values, and one wants to use the first to produce an array that could be read by the second? How should one go about it in a way that's not likely to result in compilers generating loads of silly and useless code? – Liddie