Merge #18167: Fix a violation of C++ standard rules where unions are used for type-punning

0653939ac130eddffe40c53ac418bea305d3bf82 Add static_asserts to ser_X_to_Y() methods (Samer Afach)
be94096dfb0c4862e2314cbae4120d7360b08ef2 Fix a violation of C++ standard rules that unions cannot be switched. (Samer Afach)

Pull request description:

  Type punning in C++ is not like C. As per the C++ standard, one cannot use unions to convert the bit type. A discussion about this can be found [here](https://stackoverflow.com/questions/25664848/unions-and-type-punning). In C++, a union is supposed to only hold one type at a time. It's intended to be used only as `std::variant`. Switching types is undefined behavior.

  In fact, C++20 has a special casting function, called [`bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast) that solved this problem.

  Why has it been working so far? Because some compilers tolerate using unions and switching types, like gcc. More information [here](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Type-punning).

  One important thing to mention is that performance is generally not affected by that memcpy. Compilers are smart enough to convert that to a memory cast when possible. But we have to do it the right way, otherwise, it's jut undefined behavior that depends on the compiler.

ACKs for top commit:
  practicalswift:
    ACK 0653939ac130eddffe40c53ac418bea305d3bf82
  elichai:
    ACK 0653939ac130eddffe40c53ac418bea305d3bf82
  laanwj:
    Code review ACK 0653939ac130eddffe40c53ac418bea305d3bf82
  kristapsk:
    ACK 0653939ac130eddffe40c53ac418bea305d3bf82

Tree-SHA512: f6e89de39fc964750429139bab6b5a1346f7060334b7afa020e315bdad8f8c195bce2b8a9e343f06e7fff175e2dfb1cdabfcb6fe405bea0febe4962f0cc62557
This commit is contained in:
Wladimir J. van der Laan 2020-02-26 19:00:18 +01:00 committed by munkybooty
parent 9fc7ffaac9
commit bbaab7ca5c

View File

@ -10,6 +10,7 @@
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <cstring>
#include <ios> #include <ios>
#include <limits> #include <limits>
#include <list> #include <list>
@ -127,27 +128,31 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
} }
inline uint64_t ser_double_to_uint64(double x) inline uint64_t ser_double_to_uint64(double x)
{ {
union { double x; uint64_t y; } tmp; uint64_t tmp;
tmp.x = x; std::memcpy(&tmp, &x, sizeof(x));
return tmp.y; static_assert(sizeof(tmp) == sizeof(x), "double and uint64_t assumed to have the same size");
return tmp;
} }
inline uint32_t ser_float_to_uint32(float x) inline uint32_t ser_float_to_uint32(float x)
{ {
union { float x; uint32_t y; } tmp; uint32_t tmp;
tmp.x = x; std::memcpy(&tmp, &x, sizeof(x));
return tmp.y; static_assert(sizeof(tmp) == sizeof(x), "float and uint32_t assumed to have the same size");
return tmp;
} }
inline double ser_uint64_to_double(uint64_t y) inline double ser_uint64_to_double(uint64_t y)
{ {
union { double x; uint64_t y; } tmp; double tmp;
tmp.y = y; std::memcpy(&tmp, &y, sizeof(y));
return tmp.x; static_assert(sizeof(tmp) == sizeof(y), "double and uint64_t assumed to have the same size");
return tmp;
} }
inline float ser_uint32_to_float(uint32_t y) inline float ser_uint32_to_float(uint32_t y)
{ {
union { float x; uint32_t y; } tmp; float tmp;
tmp.y = y; std::memcpy(&tmp, &y, sizeof(y));
return tmp.x; static_assert(sizeof(tmp) == sizeof(y), "float and uint32_t assumed to have the same size");
return tmp;
} }