Search code examples
c++memory-alignmentreinterpret-cast

reinterpret_cast and Structure Alignment


Is there any safe way to cast from an integer to a structure?

As an example:

struct Colour
{
    uint8_t A;
    uint8_t R;
    uint8_t G;
    uint8_t B;
};

And I cast to or from an integer:

uint32_t myInteger
Colour* myColour = reinterpret_cast<Colour*>(&myInteger);
uint32_t* myInteger2 = reinterpret_cast<uint32_t*>(myColour);

If my structure is padded, then this won't work, is there any way to guarantee this works?

I understand this might not be standard, but I'd prefer support for major compilers (Visual Studio and GCC) rather than some bitshifting workaround, which has already been answered here: Type casting struct to integer c++.


Solution

  • Given the restrictions given in the comments (only care about VC++ and gcc on Windows and Linux), and assuming you're willing to further restrict that to "running on x86 and possibly ARM", you can probably get by pretty easily by adding a pragma to ensure against padding in the structure:

    #pragma pack(push, 1)
    struct Colour
    {
        uint8_t A;
        uint8_t R;
        uint8_t G;
        uint8_t B;
    };
    #pragma pack(pop)
    

    Note that if you didn't care about compatibility with VC++, you might want to do this differently (gcc/g++ has an __attribute__(aligned(1)) that might otherwise be preferred).

    As far as reinterpret_cast goes, there's a fairly simple rule: the operand and target type must always be either a pointer or a reference (well, you can pass the name of a glvalue, but what's used is a reference to that object)--the whole idea here is to get something that refers to the original object, but "views" it as if it were a different type, and to do that, you have to pass something that gives access to the operand, not just its value.

    If the result you want is a value (rather than a reference or pointer) you can dereference the result, and assign the result of that dereference to your target.

    uint32_t value = *reinterpret_cast<uint32_t *>(&some_color_object);
    

    or:

    color c = *reinterpret_cast<Color *>(&some_uint32_t);
    

    Given the nature of references, it's possible for some of this to be hidden:

    color c = reinterpret_cast<Color &>(some_uint32_t);
    

    Here's a quick bit of test code to do some conversions and test/display the results (using both pointers and references, for whatever that may be worth):

    #include <iostream>
    #include <cassert>
    
    #pragma pack(push, 1)
    struct Colour
    {
        uint8_t A;
        uint8_t R;
        uint8_t G;
        uint8_t B;
    
        bool operator==(Colour const &e) const {
            return A == e.A && R == e.R && G == e.G && B == e.B;
        }
    
        friend std::ostream &operator<<(std::ostream &os, Colour const &c) {
            return os << std::hex << (int)c.A << "\t" << (int)c.R << "\t" << (int)c.G << "\t" << (int)c.B;
        }
    };
    #pragma pack(pop)
    
    int main() {
        Colour c{ 1,2,3,4 };
    
        uint32_t x = *reinterpret_cast<uint32_t *>(&c);
    
        uint32_t y = 0x12345678;
    
        Colour d = *reinterpret_cast<Colour *>(&y);
    
        Colour e = reinterpret_cast<Colour &>(y);
    
        assert(d == e);
        std::cout << d << "\n";
    }
    

    Do note the restrictions given above though. I've tested this with both VC++ (2015) and g++ (5.3), and I'd guess it'll probably work on other versions of those compilers--but there's not much of anything in the way of guarantees with code like this.

    It's also entirely possible that it could break even with those compilers, but on a different CPU. In particular, the alignment requirements for your Colour and for a uint32_t could be different, so on a CPU that has alignment requirements, it might not work (and even on an Intel, alignment could affect speed).