diff --git a/include/fmt/format.h b/include/fmt/format.h index 89264541..e7491175 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -211,6 +211,17 @@ inline uint32_t clzll(uint64_t x) { namespace fmt { namespace internal { +// An equivalent of `*reinterpret_cast(&source)` that doesn't produce +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + // An implementation of begin and end for pre-C++11 compilers such as gcc 4. template FMT_CONSTEXPR auto begin(const C &c) -> decltype(c.begin()) { diff --git a/test/util-test.cc b/test/util-test.cc index d192a6bb..772ccb5c 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -397,6 +397,16 @@ TEST(FixedBufferTest, BufferOverflow) { EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow"); } +TEST(UtilTest, BitCast) { + struct S { + uint32_t u[2]; + }; + auto s = fmt::internal::bit_cast(uint64_t(42)); + EXPECT_EQ(fmt::internal::bit_cast(s), 42); + s = fmt::internal::bit_cast(uint64_t(~0ull)); + EXPECT_EQ(fmt::internal::bit_cast(s), ~0ull); +} + TEST(UtilTest, Increment) { char s[10] = "123"; increment(s);