Reputation: 3256
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
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
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
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