Nuclear
Nuclear

Reputation: 1458

C++ POD struct inheritance? Are there any guarantees about the memory layout of derived members

Let's say, I have a struct RGB and I want to create struct RGBA, which inherits RGB:

struct RGB {
    unsigned char r;
    unsigned char g;
    unsigned char b;
};

struct RGBA: RGB {
    unsigned char a;
};

Both will be used for reading uncompressed image data:

RGBA *pixel=static_cast<RGBA *>(image->uncompressed_data);

Question: Is this safe, regarding the memory layout of struct RGBA? Does anyone guarantee, that:

will #pragma pack help here? It's all about memory layout during inheritance.

Upvotes: 32

Views: 10290

Answers (5)

uuu777
uuu777

Reputation: 891

I maybe wrong but seems to me that you can define:

struct RGBA {
    struct RGB base;
    unsigned char g;
};

and (1) RGBA will be POD, and (2) you can cast both ways:

RRBA x;
RGB* x_rgb = reinterpret_cast<RGB*>(&x);
RGBA* x_rgba = reinterpret_cast<RGBA*>(x_rgb);

I suspect there will be some padding that may be eliminated by attribute((packed)).

Upvotes: 0

wiRe
wiRe

Reputation: 101

As all previous answers here already stated: it is not guaranteed by the standard. But if you still require such an inheritance of PODs (the derived class is actually no POD anymore), you can at least validate that the current compiler behaves as desired, by using static_assert. If you switch to another compiler that behaves different, then you should at least get compiler errors. Though, this may be bad practice if your code should be easily portable.

I also recommend to use compiler variable attribute __attribute__((packed)) to declare your POD structures and to use <cstdint>. This is valid with e.g. GCC, but might also be different depending on the compiler you use.

#include <cstdint>

struct __attribute__((packed)) RGB {
    uint8_t r;
    uint8_t g;
    uint8_t b;
};

struct __attribute__((packed)) RGBA : RGB {
    uint8_t a;
};

static_assert( sizeof(RGB) == 3u, "Unexpected size" );
static_assert( sizeof(RGBA) == 4u, "Unexpected size" );

Upvotes: 1

Mike Seymour
Mike Seymour

Reputation: 254631

No, the layout is not guaranteed. The only guarantees are for standard-layout classes; and one of the conditions of such a class is that it

either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members

In other words, all data members must be in the same class, not in more than one.

Upvotes: 22

Some programmer dude
Some programmer dude

Reputation: 409364

It's easy to check for padding: Print sizeof(RGB) and sizeof(RGBA). If it's not 3 respective 4 then the structures are padded, and you need to remove it.

As for if the member a comes after b, you can use offsetof to check each members offset. If the offset for a is one larger than the offset of b then a comes directly after b.

Upvotes: 2

billz
billz

Reputation: 45430

There is NO guarantees about the memory layout of derived members and the cast is NOT safe.

As you have inheritance, also there could be padding, this is NOT trivial.

§ 9 Classes

1 A POD struct109 is a class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has no non-

Also std::is_pod<RGBA> is not a POD

std::cout << std::boolalpha;
std::cout << std::is_pod<RGBA>::value << '\n';

result is false. see live demo

Upvotes: 10

Related Questions