I've also been thinking about the fragile base class problem, and as
food for thought I'd like to present what I consider the easiest
solution. Note that my approach doesn't work with cross-parcel access of
member variables, not even with accessing member vars of superclasses if
the superclass comes from another parcel. But I think that's something
we could live with. Accessing the member vars of other classes is bad
for encapsulation anyway. Using an accessor method will of course be
supported.
With this restriction, you only have to care about the total size of the
member vars of a super class, not about the actual layout. So if we keep
the current way of appending member vars of subclasses, all we have to
know is the total size of the member vars of all superclasses and use
that as offset for the member vars of a subclass.
To implement this, member vars are broken up into structs for every
class, and every class has a global that contains the offset of the
class' member var struct. These offsets can then be computed at run-time
during initialization to adjust for possible changes in the size of
superclasses from other parcels.
Access of member vars would then look something like this (very much
like the code in Marvin's previous mail, only with a single offset per
class):
static inline void*
SI_member_address(void *self, size_t offset) {
return (char*)self + offset;
}
typedef struct Base_MEMBERS {
int base_num;
float base_float;
} Base_MEMBERS;
// Will be 0 for the top-most class in the hierarchy
extern size_t Base_MEMBERS_OFFSET;
static inline Base_MEMBERS*
Base_get_members(void *self) {
return (Base_MEMBERS*)
SI_member_address(self, Base_MEMBERS_OFFSET);
}
typedef struct Sub_MEMBERS {
int sub_num;
float sub_float;
} Sub_MEMBERS;
// Will be (Base_MEMBERS_OFFSET + sizeof(Base_MEMBERS))
extern size_t Sub_MEMBERS_OFFSET;
static inline Sub_MEMBERS*
Sub_get_members(void *self) {
return (Sub_MEMBERS*)
SI_member_address(self, Sub_MEMBERS_OFFSET);
}
void
Sub_method(void *self) {
Sub_MEMBERS *members = Sub_get_members(self);
members->sub_num = 1;
members->sub_float = 1.0;
// Only safe if Base is in the same parcel
Base_MEMBERS *base_members = Base_get_members(self);
base_members->base_num = 1;
base_members->base_float = 1.0;
}
The biggest drawback I can see is that objects become void pointers, and
we'll lose some of the type-checking that the C compiler currently
performs for us. But that's something other solutions to the fragile
base class problem will also have to deal with.
Nick