Reputation: 1458
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)struct RGB
and the a parameter from struct RGBA
?will #pragma pack
help here? It's all about memory layout during inheritance.
Upvotes: 32
Views: 10290
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
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
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
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
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