P12616 Is this a thing? link reply
Let A be a class, B be a base class, and suppose C inherits from B. Let X be a class containing an instance of A and B, and Y a class containing an instance of A and C. Then X is a base class of Y.

#include <stdio.h>

typedef struct {
int a_data;
} A;

typedef struct {
int b_data;
} B;

typedef struct {
B super;
int c_data;
} C;

typedef struct {
A a;
B b;
} X;

typedef struct {
A a;
C c;
} Y;

int main() {

Y y = {.a = {.a_data = 0}, .c = {.super = {.b_data = 1}, .c_data = 2}};

X* x = (X*)&y; // It just werks.

printf("x->a.a_data=%d, x->b.b_data=%d\n", x->a.a_data, x->b.b_data);

return 0;
}
P12621 link reply
You could make a polymorphic type Z. Then define X to be Z with the type parameter set to B, and define Y to be Z with the type parameter set to C.
P12622 link reply
P12616
it was a bit confusing how you started talking about classes and then showed structs, but i can understand what is happening
in memory, Y has the length of 3 integers: a_data+b_data+c_data
while X has the length of 2 integers: a_data+b_data
so when you make the X pointer point to a Y and dereference it, you are truncating that Y to only a_data+b_data, which is X, so yea it just werks
but if you invert the order of super and c_data, it no longer werks
try to recompile with

typedef struct {
int c_data;
B super;
} C;

then the X pointer will be accessing c_data as if it is b_data
it still kinda works bc they are both integers, even though you are getting c_data instead now, but if they had different types you'd get mangled data
try again with

typedef struct {
float c_data;
B super;
} C;
P12627 link reply
P12622
Yeah, the changing type being at the end is important.

I managed to break it with alignment issues.

#include <stdio.h>
#include <stdint.h>

typedef struct {
int8_t a_data;
} A;

typedef struct {
int8_t b_data;
} B;

typedef struct {
B super;
int32_t c_data;
} C;

typedef struct {
A a;
B b;
} X;

typedef struct {
A a;
C c;
} Y;

int main() {

Y y = {.a = {.a_data = 0}, .c = {.super = {.b_data = 1}, .c_data = 2}};

X* x = (X*)&y; // It did not work.

printf("x->a.a_data=%d, x->b.b_data=%d\n", x->a.a_data, x->b.b_data);

return 0;
}

Output (on my machine):
x->a.a_data=0, x->b.b_data=0


Obviously this is all undefined behavior, but I suspect it works as long as the size of the A type is nice and round for alignment.
P12635 link reply
P12616
It works in C because there's no real type system.
If you made the B and C pointers in X and Y, and wrapped X and Y in a union, would it not be undefined behavior?
How does this work in object-oriented languages? Should Y actually be a subclass of X?
P12704 link reply
P12635

This is how I would do it in C++, but it incurs the overhead of virtual functions when the unsafe C version doesn't have them. I make virtual getter functions to get pointers to the members, and if the member is not contained, NULL is returned, so the caller should check the pointer before use.

#include <stdio.h>
#include <stdint.h>

class A {
public:
A(int8_t a_data_) : a_data(a_data_) {}
int8_t a_data;
};

class B {
public:
B(int8_t b_data_) : b_data(b_data_) {}
int8_t b_data;
};

class C : public B {
public:
C(int8_t b_data_, int32_t c_data_) : B(b_data_), c_data(c_data_) {}
int32_t c_data;
};


class I_ABC_Container {
public:
virtual A* getA() = 0;
virtual B* getB() = 0;
virtual C* getC() = 0;
};

class X : public I_ABC_Container {
public:
X(A a_, B b_) : a(a_), b(b_) {}
A a;
B b;

virtual A* getA() {
return &a;
}

virtual B* getB() {
return &b;
}

virtual C* getC() {
return NULL;
}
};

class Y : public I_ABC_Container {
public:
Y(A a_, C c_) : a(a_), c(c_) {}
A a;
C c;

virtual A* getA() {
return &a;
}

virtual B* getB() {
return &c;
}

virtual C* getC() {
return &c;
}
};

int main() {

Y y = Y(A(0), C(1, 2));

I_ABC_Container* container = &y; // It just werks.

printf("container->getA()->a_data=%d, container->getB()->b_data=%d\n",
container->getA()->a_data, container->getB()->b_data);

return 0;
}
x