├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── bit_cast.h
└── test.cc
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.11)
2 | project(bit_cast CXX)
3 |
4 | # Boilerplate #################################################################
5 |
6 | if(NOT CMAKE_BUILD_TYPE)
7 | message(STATUS "No build type selected, default to Release")
8 | set(CMAKE_BUILD_TYPE "Release")
9 | endif()
10 | string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
11 | if(CMAKE_BUILD_TYPE AND
12 | NOT uppercase_CMAKE_BUILD_TYPE MATCHES "^(DEBUG|RELEASE)$")
13 | message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
14 | endif()
15 |
16 | function(ADD_COMPILE_FLAG value)
17 | message(STATUS "Building with ${value}")
18 | foreach(variable CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
19 | set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
20 | endforeach(variable)
21 | endfunction()
22 |
23 | function(ADD_LINK_FLAG value)
24 | message(STATUS "Linking with ${value}")
25 | foreach(variable CMAKE_EXE_LINKER_FLAGS)
26 | set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
27 | endforeach(variable)
28 | endfunction()
29 |
30 | # clang doesn't print colored diagnostics when invoked from Ninja.
31 | if(UNIX AND
32 | CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND
33 | CMAKE_GENERATOR STREQUAL "Ninja")
34 | add_compile_flag("-fcolor-diagnostics")
35 | endif()
36 |
37 | include(CheckCXXCompilerFlag)
38 |
39 | # Configuration ###############################################################
40 |
41 | add_compile_flag("-Wall")
42 | add_compile_flag("-Werror")
43 | add_compile_flag("-Wextra")
44 | if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
45 | add_compile_flag("-O0")
46 | add_compile_flag("-g3")
47 | else()
48 | add_compile_flag("-O2")
49 | endif()
50 |
51 | CHECK_CXX_COMPILER_FLAG(-fconcepts COMPILER_SUPPORTS_CONCEPTS)
52 | if(COMPILER_SUPPORTS_CONCEPTS)
53 | add_compiler_flag("-fconcepts")
54 | endif()
55 |
56 | add_compile_flag("-std=c++1z")
57 | add_compile_flag("-DBIT_CAST_USE_ENABLE_IF=1")
58 | add_compile_flag("-DBIT_CAST_USE_STATIC_ASSERT=1")
59 |
60 | # Build / test ################################################################
61 |
62 | add_executable(test "test.cc")
63 | add_test(test test)
64 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 JF Bastien
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bit-casting object representations
2 |
3 | Sample implementation for the C++ standards committee paper on `bit_cast`.
5 |
6 | The implementation conforms to the API as described in the paper, and showcases
7 | concept checking using:
8 |
9 | * C++ concepts from the Concepts TS, as available in GCC 6 and later.
10 | * SFINAE.
11 | * `enable_if`.
12 |
13 | The associated tests try to hit interesting / horrible uses of this feature.
14 |
--------------------------------------------------------------------------------
/bit_cast.h:
--------------------------------------------------------------------------------
1 | #ifndef BIT_CAST_H
2 | #define BIT_CAST_H
3 |
4 | #include
5 | #include
6 |
7 | // TODO: Use is_trivially_copyable_v and other type traits when my compiler
8 | // implements more of C++17.
9 |
10 | #if defined(__cpp_concepts) && __cpp_concepts >= 201507
11 | # define BIT_CAST_CONCEPTS(TO, FROM) requires \
12 | sizeof(TO) == sizeof(FROM) && \
13 | std::is_trivially_copyable::value && \
14 | std::is_trivially_copyable::value
15 | #else
16 | # define BIT_CAST_CONCEPTS(TO, FROM)
17 | #endif
18 |
19 | #if defined(BIT_CAST_USE_SFINAE)
20 | # define BIT_CAST_ENABLE_IF(TO, FROM) , \
21 | typename = std::enable_if_t, \
22 | typename = std::enable_if_t::value>, \
23 | typename = std::enable_if_t::value>
24 | #else
25 | # define BIT_CAST_ENABLE_IF(TO, FROM)
26 | #endif
27 |
28 | #if defined(BIT_CAST_USE_STATIC_ASSERT)
29 | # define BIT_CAST_STATIC_ASSERTS(TO, FROM) do { \
30 | static_assert(sizeof(TO) == sizeof(FROM)); \
31 | static_assert(std::is_trivially_copyable::value); \
32 | static_assert(std::is_trivially_copyable::value); \
33 | } while (false)
34 | #else
35 | # define BIT_CAST_STATIC_ASSERTS(TO, FROM) (void)0
36 | #endif
37 |
38 | namespace {
39 |
40 | // Defined in header .
41 | //
42 | // 1. Requires: `sizeof(To) == sizeof(From)`,
43 | // `is_trivially_copyable_v` is `true`,
44 | // `is_trivially_copyable_v` is `true`.
45 | //
46 | // 2. Returns: an object of type `To` whose *object representation* is equal to
47 | // the object representation of `From`.
48 | // If multiple *object representations* could represent the *value
49 | // representation* of `From`, then it is unspecified which `To`
50 | // value is returned.
51 | // If no *value representation* corresponds to `To`'s *object
52 | // representation* then the returned value is unspecified.
53 | template
54 | BIT_CAST_CONCEPTS(TO, FROM)
55 | inline constexpr To bit_cast(const From& from) noexcept {
56 | BIT_CAST_STATIC_ASSERTS(To, From);
57 | typename std::aligned_storage::type storage;
58 | std::memcpy(&storage, &from, sizeof(To)); // Above `constexpr` is optimistic, fails here.
59 | return reinterpret_cast(storage);
60 | // More common implementation:
61 | // std::remove_const_t to{};
62 | // std::memcpy(&to, &from, sizeof(To)); // Above `constexpr` is optimistic, fails here.
63 | // return to;
64 | }
65 |
66 | }
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------
/test.cc:
--------------------------------------------------------------------------------
1 | #include "bit_cast.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | size_t failures = 0;
10 | constexpr int width = 16;
11 |
12 | #define T(TO, FROM, INPUT, ACCESS, FMT, EXPECT) do { \
13 | char buf[128] = { '\0' }; \
14 | auto n = snprintf(buf, sizeof(buf), FMT, \
15 | bit_cast(FROM INPUT) ACCESS); \
16 | if (n <= 0 || n > width) { \
17 | ++failures; \
18 | std::cout << "\tformatting FAILED!"; \
19 | } else { \
20 | std::cout \
21 | << std::string(width - n, ' ') << buf << ' ' \
22 | << "bit_cast<" #TO ">(" #FROM " " #INPUT ")" #ACCESS; \
23 | if (std::string(buf) != std::string(EXPECT)) { \
24 | ++failures; \
25 | std::cout << "\tFAILED! expected: " << (EXPECT); \
26 | } \
27 | } \
28 | std::cout << '\n'; \
29 | } while (false)
30 |
31 | struct Float { unsigned mantissa : 23; unsigned exponent : 8; unsigned sign : 1; };
32 | struct Padded { uint8_t c; uint16_t s; };
33 | struct NoCtor { NoCtor() = delete; uint32_t u; };
34 | // Deleting copy ctor or move ctor doesn't make sense when trivially copyable.
35 | // Deleting dtor doesn't make sense either.
36 | class Private { uint32_t u; public: uint32_t get() const { return u; } };
37 | struct Const { const uint32_t u; };
38 | struct Volatile { volatile uint32_t u; };
39 | struct ConstVolatile { const volatile uint32_t u; };
40 | struct DefaultMemberInit { uint32_t u = 1337; };
41 | union Union { uint32_t u; float f; };
42 | union UnionNoCtor { struct S { S() = delete; uint32_t u; } s; float f; };
43 | struct StructArray { uint8_t arr[4]; };
44 | struct ZeroWidth { uint32_t u; uint32_t end[0]; };
45 | struct ZeroWidthC { uint32_t u; uint32_t end[]; };
46 | struct Recurse { Recurse() { u = bit_cast(0.f); } uint32_t u; };
47 | struct RecurseInit { uint32_t u = bit_cast(0.f); };
48 | struct RecurseAggInit { uint32_t u { bit_cast(0.f) }; };
49 | typedef __attribute__((vector_size(4))) uint8_t V4x8;
50 | // Array To doesn't make sense?
51 |
52 | int main() {
53 | // TO FROM INPUT ACCESS FMT EXPECT
54 | T( float, uint32_t, (0x00000000), , "%a", "0x0p+0");
55 | T( uint32_t, float, (0.0), , "0x%08x", "0x00000000");
56 | T( uint32_t, float, (-0.0), , "0x%08x", "0x80000000");
57 | T( uint32_t, float, (2.0), , "0x%08x", "0x40000000");
58 | T( const uint32_t, (const float), (2.0), , "0x%08x", "0x40000000");
59 | T( uint32_t, (volatile float), (2.0), , "0x%08x", "0x40000000");
60 | T( float, Float, ({0,0,0}), , "%a", "0x0p+0");
61 | T( float, Float, ({0,0,1}), , "%a", "-0x0p+0");
62 | T( float, Float, ({0,0x80,0}), , "%a", "0x1p+1");
63 | T( Padded, float, (2.f), .c, "0x%02x", "0x00");
64 | T( Padded, float, (2.f), .s, "0x%04x", "0x4000");
65 | T( NoCtor, float, (2.f), .u, "0x%08x", "0x40000000");
66 | T( Private, float, (2.f), .get(), "0x%08x", "0x40000000");
67 | T( Const, float, (2.f), .u, "0x%08x", "0x40000000");
68 | T( Volatile, float, (2.f), .u, "0x%08x", "0x40000000");
69 | T( ConstVolatile, float, (2.f), .u, "0x%08x", "0x40000000");
70 | T( DefaultMemberInit, float, (2.f), .u, "0x%08x", "0x40000000");
71 | T( Union, float, (2.f), .u, "0x%08x", "0x40000000");
72 | T( UnionNoCtor, float, (2.f), .s.u, "0x%08x", "0x40000000");
73 | T( StructArray, float, (2.f), .arr[0], "0x%02x", "0x00");
74 | T( StructArray, float, (2.f), .arr[1], "0x%02x", "0x00");
75 | T( StructArray, float, (2.f), .arr[2], "0x%02x", "0x00");
76 | T( StructArray, float, (2.f), .arr[3], "0x%02x", "0x40");
77 | T( ZeroWidth, float, (2.f), .u, "0x%08x", "0x40000000");
78 | T( ZeroWidthC, float, (2.f), .u, "0x%08x", "0x40000000");
79 | T( Recurse, float, (2.f), .u, "0x%08x", "0x40000000");
80 | T( RecurseInit, float, (2.f), .u, "0x%08x", "0x40000000");
81 | T( RecurseAggInit, float, (2.f), .u, "0x%08x", "0x40000000");
82 | T( V4x8, uint32_t, (0xdeadbeef), [0], "0x%02x", "0xef");
83 | T( V4x8, uint32_t, (0xdeadbeef), [1], "0x%02x", "0xbe");
84 | T( V4x8, uint32_t, (0xdeadbeef), [2], "0x%02x", "0xad");
85 | T( V4x8, uint32_t, (0xdeadbeef), [3], "0x%02x", "0xde");
86 | T(std::complex, uint32_t, (0xdeadbeef), .real(), "0x%04x", "0xbeef");
87 | T(std::complex, uint32_t, (0xdeadbeef), .imag(), "0x%04x", "0xdead");
88 |
89 | uint8_t arr[4] = {1,2,3,4};
90 | std::cout << "Array:\t" << std::hex << "0x" << bit_cast(arr) << '\n';
91 | std::cout << "&main as uintptr_t:\t" << std::hex << "0x" << bit_cast(&main) << '\n';
92 |
93 | // Reference / rvalue-ref To: could use remove_reference, but it doesn't really make any sense.
94 | // uint32_t &v(bit_cast(2.f));
95 | // uint32_t &&v(bit_cast(2.f));
96 | //
97 | // constexpr is currently broken because of memcpy. The standard should
98 | // specify that constexpr bit_cast "Just Works", it doesn't hinge on memcpy
99 | // being constexpr but implementations will at least need a compiler builtin.
100 | // non-constexpr function 'memcpy' cannot be used in a constant expression:
101 | // { constexpr uint32_t c = 0; constexpr float f = bit_cast(c); (void)f; }
102 | //
103 | // std::forward?
104 |
105 | if (failures)
106 | std::cout << "Failures: " << failures << std::endl;
107 | return !!failures;
108 | }
109 |
--------------------------------------------------------------------------------