4

I would like to know if the following code is an acceptable method to handle type punning in a way that doesn't break strict aliasing rules. I realize that this method relies on a GCC compiler extension, so there is no need to point that out.

template <class output_type, class input_type>
inline output_type punning_cast(const input_type& input)
{
    static_assert(std::is_pod<output_type>::value, "output_type for punning_cast must be POD");
    static_assert(std::is_pod<input_type>::value, "input_type for punning_cast must be POD");
    static_assert(sizeof(output_type) == sizeof(input_type), "input_type and output_type must be the same size");

    typedef output_type __attribute__((may_alias)) output_type_may_alias;

    return *reinterpret_cast<const output_type_may_alias*>(&input);
}

template <class output_type, class input_type>
inline output_type punning_cast(const input_type* input)
{
    static_assert(std::is_pod<output_type>::value, "output_type for punning_cast must be POD");
    static_assert(std::is_pod<input_type>::value, "input_type for punning_cast must be POD");

    typedef output_type __attribute__((may_alias)) output_type_may_alias;

    return *reinterpret_cast<const output_type_may_alias*>(input);
}

Example usage:

uint32_t float_as_int = punning_cast<uint32_t>(3.14f);

unsigned char data[4] = { 0xEF, 0xBE, 0xAD, 0xDE };
uint32_t magic = punning_cast<uint32_t>(data);
Chris_F
  • 4,991
  • 5
  • 33
  • 63

1 Answers1

5

I tend to use a union for this. Something like:

template <class output_type, class input_type>
inline output_type punning_cast(const input_type& input)
{
    union {
        input_type in;
        output_type out;
    } u;

    u.in = input;
    return u.out;
}

Strictly speaking, this is Undefined Behavior in C++ (although not in C). But then so is yours, and I have yet to see a C++ compiler that does not "do what I want" for this construct... So I believe this is a fairly portable way to do type punning.

The best alternative, I think, is just to use memcpy, and that is probably what I would do for the "pointer" version of your code. A good compiler will inline the call, so there should be no performance penalty.

I find the union formulation easier to read.

[Update]

The GCC documentation says:

The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type.

So GCC, at least, specifically supports using a union.

Community
  • 1
  • 1
Nemo
  • 70,042
  • 10
  • 116
  • 153
  • 1
    The memcpy solution would be well defined and portable, and would likely be just as fast (optimized away completely), but when compiler optimizations are disabled that will no longer be the case. – Chris_F May 10 '14 at 03:08
  • 1
    It appears that GCC, ICC and Clang all support the may_alias attribute, so it will probably be my preferred method. I have no idea if MSVC supports this, then again, MSVC doesn't support anything of note. – Chris_F May 10 '14 at 04:31
  • @Chris_F may_alias could make sense if punning_cast returned a reference, but since you are returning by value, I don't see the advantage compared to the union or memcpy. – Marc Glisse May 10 '14 at 22:08
  • @Chris_F: How much do you care about speed when you disable optimization? – Nemo May 10 '14 at 23:41
  • @Nemo A quick test with GCC shows that the casting version (with may_alias extension) produces ideal code pretty much every time. The union version and memcpy seem to be slightly less optimized in some cases (especially with memcpy and -O0). – Chris_F May 11 '14 at 01:41
  • 1
    Upon further testing I think that memcpy is in fact the best solution. – Chris_F May 12 '14 at 04:41