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

Reply via email to