Lefteris
Lefteris

Reputation: 3256

Mocking "inheritance" in accessing members of structures in C

I have a question about C and attempting to mock a partial type of "inheritance", only in accessing members of structures. Look at the following example:

#pragma pack(push,1)
typedef struct foo
{
    int value;
    int value2;
}foo;
typedef struct foo_extended
{
    // "inherits" foo
    int value;
    int value2;
    // "inherits" foo stops
    //we also have some additional data
    float additional;
}foo_extended;
#pragma pack(pop)


//! This function works for both foo types
void workboth(void* objP)
{
    foo* obj = (foo*)objP;

    obj->value = 5;
    obj->value2 = 15;
}

//! This works only for the extended
void workextended(foo_extended* obj)
{
    obj->value = 25;
    obj->value2 = 35;
    obj->additional = 3.14;
}

int main()
{
    foo a;
    foo_extended b;

    workboth(&a);
    workboth(&b);
    workextended(&b);

    return 0;
}

This works in my system but my question is whether this can be portable as long as there is correct packing of the involved structures (depending on the compiler). I suppose it would need #ifndefs correcttly invoking the tight packing in other compilers too.

Of course the obvious problem is total lack of type checking and putting all of the responsibility of correct usage to the programmer but I am wondering if this is portable or not. Thanks!

P.S.: Forgot to mention that the standard I attempt to adhere to is C99

Upvotes: 0

Views: 165

Answers (3)

Jens Gustedt
Jens Gustedt

Reputation: 78943

As of C11, and also supported by some existing compilers as extensions to older standards, you should use an anonymous struct for that

struct foo_extended {
    struct {
      int value;
      int value2;
    };
    //we also have some additional data
    float additional;
};

by this your substructure has exactly the same layout as foo in particular what concerns alignment of its parts: to be compatible between different compilation units struct that have exactly the same fields in the same order must be laid out identically.

(The impact of your packed pragma is not so clear to me)

Since your foo structure is the first in foo_extended it must always be at offset 0 within that one.

Upvotes: 1

pmg
pmg

Reputation: 108978

By reading 6.7.2.1/12 and /13 in the C99 draft, I think it can be assumed that two different structs with the same initial members are compatible up to the first different member.

6.7.2.1/12

Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.

6.7.2.1/13

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

Upvotes: 0

MByD
MByD

Reputation: 137382

I have a slightly different method. Instead of using the same values, I create a struct in the struct, and this way the packing is unnecessary:

typedef struct foo
{
    int value;
    int value2;
}foo;

typedef struct foo_extended
{
    foo father;
    float additional;
}foo_extended;

now the rest is pretty much as you showed, with a small difference:

void workextended(foo_extended* obj)
{
    obj->father.value = 25;
    obj->father.value2 = 35;
    obj->additional = 3.14;
}

but I would add an id as a field of the first object in the hierarchy to make sure the casting is done to the correct object.

This method is guaranteed to work by the C standard.

Upvotes: 1

Related Questions