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

5

30

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:

  • unsigned char a comes after the RGB struct (not before)
  • There is no padding between struct RGB and the a parameter from struct RGBA?

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

Atween answered 14/3, 2014 at 12:1 Comment(3)
There is no guarantee in the standard that there is no padding between r and g. There is also no guarantee in the standard about the required alignment of RGBA. So even without having to answer the actual question you asked, this is not safe.Threepiece
Just create an RGBA class with a bool which indicates weather the alpha channel is used or not.Ochlocracy
You also need to consider endianness. You will save yourself a lot of trouble by just writing pack/unpack functions that do the required bitwise operations.Vituperation
H
20

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.

Hesperides answered 14/3, 2014 at 12:11 Comment(0)
K
9

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

Kob answered 14/3, 2014 at 12:6 Comment(0)
R
2

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.

Raisaraise answered 14/3, 2014 at 12:8 Comment(4)
checking it using compiler XYZ just tells how that compiler implemented it - not whether it is standard conforming behavior.Petticoat
@BЈовић As the OP wonders if #pragma pack is needed, I'm guessing that the OP have a specific compiler in mind and don't worry about portability.Raisaraise
I have no idea what #pragma pack does, and for which compiler it is for. But I think they wanted to know what the standard has to say.Petticoat
@BЈовић #pragma pack forces structs to have certain alignment, overriding whatever alignment the compiler would usually assign. It's supported by MSVC, GCC and Clang.Almira
R
1

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" );
Reena answered 16/6, 2021 at 20:38 Comment(0)
S
0

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)).

Sainfoin answered 17/12, 2021 at 3:36 Comment(1)
Yes, have a look here for more information: https://mcmap.net/q/239271/-struct-inheritance-in-cTillford

© 2022 - 2024 — McMap. All rights reserved.