Allocation, initialisation and release is done by separate member functions under my control, but I do not control access to members, as they are accessed all over the place.

The trouble is, I cannot change the struct's definition in the header that is heavily used throughout the system, but I still want to extend the type and add some members. Since this must compile as both C++ and C, I cannot simply create a derived type struct B : public A. So my idea was to add this type to the cpp file:

2 Answers
2

Alignment requirements shouldn't cause any problems, and this is indeed the default way to fake inheritance in C.

However, your static_cast_B() is not portable (in particular void * arithmetics and conversion to unsigned in case of sizeof (void *) > sizeof (unsigned) - the latter should still work, but is something of a code smell).

I believe even simple C cast shall work: B* static_cast_B(A* a) { return (B*)a; }. I do not have access to C++ and C std(s) but I am pretty sure there is zero offset in first member of POD struct.
–
PiotrNyczSep 16 '12 at 10:28

@PiotrNycz: using offsetof() makes multiple-inheritance possible - you could have a parent_foo and parent_bar, and only one of these can be the first member
–
ChristophSep 16 '12 at 12:58

@Christoph Agree. I thought only about this particular case with single inheritance.
–
PiotrNyczSep 16 '12 at 13:36

@PiotrNycz: I have single inheritance, but I like to be on the safe side. The compiler will optimise it out, anyway.
–
bitmaskSep 16 '12 at 20:02

As long as you don't introduce a virtual table (by adding a virtual method or destructor) in struct B and parent member of that struct is the first element, then it is safe to just case B to A.

Casting A to B is safe, too, but only if the original pointer is actually pointing to B and not to something else, like only A structure itself, obviously.

In case when parent is not the first member of struct B, you would have to use container_of macro as seen in Linux kernel, for example. It works off the member pointer offsets (i.e. some good explanation can be found here).

Also, by default both structs will have the same alignment. I am not sure how the compiler would place A into B if you tweak one of the structs alignment. I guess that is compiler dependent, but should be easy to figure out.

If B has a virtual table, you must know compiler internals to convert the pointers correctly. For example, GCC on x86_64 adds 8 bytes offset thus you have to offset it manually when converting. But that won't work in case of virtual inheritance (virtual base classes), to solve that you have to resort to using RTTI etc... But this is outside of the scope of your question and I'd recommend you don't go that way.