In C++20 we will hopefully get *bit_cast* see [the proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0476r2.html) and [reference implementation](https://github.com/jfbastien/bit_cast). This utility should give us a simple and safe way to type pun. The one issue I ran into with this utility is that is requires the size of the *To* and *From* type to be the same, as well as checking that *To* and *From* types are trivially copyable. The **static_assert** version of the check is as follows: ```cpp # define BIT_CAST_STATIC_ASSERTS(TO, FROM) do { \ static_assert(sizeof(TO) == sizeof(FROM)); \ static_assert(std::is_trivially_copyable::value); \ static_assert(std::is_trivially_copyable::value); \ } while (false) ``` This is not an unreasonable constraint but there may be cases where we would like to, let's say, type pun an array of char into a primitive type like *unsigned int*. After discussing this with the JF Bastien the proposal's author as well as the author of the reference implementation, one way around this restriction is to copy the chunk we want to pun into a struct with the same size as the primitive we are punning to. Let’s see how this would work. ```cpp struct four_chars { unsigned char arr[4] = {} ; } ; unsigned int foo( unsigned char *p ) { four_chars f ; std::memcpy( f.arr, p, 4) ; unsigned int result = bit_cast(f) ; return result ; } ``` What is great about this case is that the optimizer is smart enough to recognize the **memcpy** and **bit_cast** can be reduced to a single **mov** directly into a register [see gobolt](https://godbolt.org/g/hDpAmR): ```cpp foo(unsigned char*): # @foo(unsigned char*) mov eax, dword ptr [rdi] ret ``` constexpr bit_cast ------------------ It is worth it to point out an interesting aspect of the *constexpr* case. First this requires compiler support since memcpy() is not marked *constexpr* and [reinterpret_cast is not allowed in a constant expression](https://stackoverflow.com/a/24400015/1708801), likely via a [builtin](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-functions). This mainly works since the underlying assumption is that the type puns allowed by *bit_cast* can be implemented as a *mov* to a register. This feature is interesting because it now allows type punning at compile time. This also means no undefined behavior since [undefined behavior is not allowed in a constant expression](https://stackoverflow.com/q/21319413/1708801) and we expect any attempt to invoke UB will be caught at compile time. Some cases undefined behavior could pop up are a *bit_cast* from a type whose underlying representation has no value in the **To** type. For example the standard does not specify the underlying representation of [bool](https://stackoverflow.com/q/33380742/1708801) therefore a *bit_cast* to *bool* could invoke undefined behavior. e.g. ```cpp bool b = bit_cast('a') ; // UBsan catches this case: https://wandbox.org/permlink/P7hlo7AZDx2t0PoY // runtime error: load of value 97, which is not a valid value for type 'bool' ``` We also have unspecified behavior for cases where the **To** type could have multiple possibe values for a given **From** value e.g.: ```cpp bit_cast(true) // We are not guaranteed any specific value here. // Although we may have certain expectations. bit_cast(nullptr) // We would expect zero but it is not specified what the underlying value is ``` ---- I posted this write-up and a poll on type punning of char arrays on Twitter and some of the responses are interesting: - [Original Tweet on this write-up](https://twitter.com/shafikyaghmour/status/960386479066300417) - [Poll on type punning of char arrays](https://twitter.com/shafikyaghmour/status/960749871002103809) - [Also see my write-up on Strict aliasing and Type punning](https://stackoverflow.com/a/51228315/1708801)