I think "type pruning" sounds more fun than mere "type punning". It
sounds like a way to clear up a mess of too many types, leaving only the
pretty ones :-)
I guess the answer here is a class which has a single storage unit
(uint64_t) with constructors and conversion operators to the different
types, using memcpy() to make it all legal. A decent compiler will
optimise away the memcpy().
Alternatively, if you can limit yourself to gcc, clang, icc, and other
compilers that copy gcc's attributes, the "may_alias" attribute on a
type lets you use it for type punning (it gives the type the superpowers
of a character type for accesses).
#include <cstdint>
#include <cstring>
#include <array>
class DIBC {
private :
uint64_t storage;
template <class T> constexpr uint64_t toUint64_t(const T x) {
static_assert(sizeof(x) == 8);
uint64_t y;
std::memcpy(&y, &x, 8);
return y;
}
template <class T> constexpr T fromUint64_t(uint64_t x) {
static_assert(sizeof(T) == 8);
T y;
std::memcpy(&y, &x, 8);
return y;
}
public :
using char8 = std::array<char, 8>;
constexpr DIBC() : storage(0) {}
constexpr DIBC(uint64_t x) : storage(x) {}
constexpr DIBC(int64_t x) : storage(x) {}
constexpr DIBC(bool x) : storage(x) {}
constexpr DIBC(char8 x) : storage(toUint64_t(x)) {}
constexpr operator uint64_t() const { return storage; }
constexpr operator int64_t() const { return storage; }
constexpr operator bool() const { return storage; }
constexpr operator char8 const () { return
fromUint64_t<char8>(storage); }
};