From b3292298c976279765926c9927f2e4cfe38b3ede Mon Sep 17 00:00:00 2001 From: Lioncash Date: Thu, 10 May 2018 09:54:35 -0400 Subject: [PATCH] BitUtils: Add C++14/C++17 compatible equivalent of std::bit_cast from C++2a Given bit conversions between types are quite common in emulation (particularly when it comes to floating-point among other things) it makes sense to provide a utility function that keeps all the boilerplate contained; especially considering it makes it harder to accidentally misuse std::memcpy (such as accidentally transposing arguments, etc). Another benefit of this function is that it doesn't require separating declarations from assignments, allowing variables to be declared const. This makes the scenario of of uninitialized variables being used less likely to occur. --- Source/Core/Common/BitUtils.h | 34 ++++++++++++++++++++++++ Source/UnitTests/Common/BitUtilsTest.cpp | 13 +++++++++ 2 files changed, 47 insertions(+) diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index 2d45902e41..c975b5c475 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace Common @@ -165,4 +166,37 @@ constexpr bool IsValidLowMask(const T mask) noexcept // and doesn't require special casing either edge case. return (mask & (mask + 1)) == 0; } + +/// +/// Reinterpret objects of one type as another by bit-casting between object representations. +/// +/// @remark This is the example implementation of std::bit_cast which is to be included +/// in C++2a. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0476r2.html +/// for more details. The only difference is this variant is not constexpr, +/// as the mechanism for bit_cast requires a compiler built-in to have that quality. +/// +/// @param source The source object to convert to another representation. +/// +/// @tparam To The type to reinterpret source as. +/// @tparam From The initial type representation of source. +/// +/// @return The representation of type From as type To. +/// +/// @pre Both To and From types must be the same size +/// @pre Both To and From types must satisfy the TriviallyCopyable concept. +/// +template +inline To BitCast(const From& source) noexcept +{ + static_assert(sizeof(From) == sizeof(To), + "BitCast source and destination types must be equal in size."); + static_assert(std::is_trivially_copyable(), + "BitCast source type must be trivially copyable."); + static_assert(std::is_trivially_copyable(), + "BitCast destination type must be trivially copyable."); + + std::aligned_storage_t storage; + std::memcpy(&storage, &source, sizeof(storage)); + return reinterpret_cast(storage); +} } // namespace Common diff --git a/Source/UnitTests/Common/BitUtilsTest.cpp b/Source/UnitTests/Common/BitUtilsTest.cpp index 4785f9dcef..e847e908bd 100644 --- a/Source/UnitTests/Common/BitUtilsTest.cpp +++ b/Source/UnitTests/Common/BitUtilsTest.cpp @@ -127,3 +127,16 @@ TEST(BitUtils, IsValidLowMask) EXPECT_FALSE(Common::IsValidLowMask((u64) ~(0b10000))); EXPECT_FALSE(Common::IsValidLowMask((u64)(~((u64)(~0b0) >> 1) | 0b1111))); } + +TEST(BitUtils, BitCast) +{ + EXPECT_EQ(0x00000000U, Common::BitCast(0.0f)); + EXPECT_EQ(0x80000000U, Common::BitCast(-0.0f)); + EXPECT_EQ(0x3F800000U, Common::BitCast(1.0f)); + EXPECT_EQ(0xBF800000U, Common::BitCast(-1.0f)); + + EXPECT_EQ(0x0000000000000000ULL, Common::BitCast(0.0)); + EXPECT_EQ(0x8000000000000000ULL, Common::BitCast(-0.0)); + EXPECT_EQ(0x3FF0000000000000ULL, Common::BitCast(1.0)); + EXPECT_EQ(0xBFF0000000000000ULL, Common::BitCast(-1.0)); +}