Polymorphic structs are quite common in C but often involve explicit casts which allow for accidentally casting incompatible structs.
struct ID {
char name[32];
};
struct IntID {
struct ID id_base;
int value;
}
struct FloatID {
struct ID id_base;
float value;
}
void id_name_set(ID *id, const char *name)
{
strlcpy(id->name, name, sizeof(id->name));
}
/* macro that happens to use 'id_name_set', this is a bit contrived */
#define ID_NAME_SET_AND_VALUE(id, name, val) \
do { \
id_name_set((ID *)id, name); \
id->value = val; \
} while(0)
void func(void)
{
struct { int value; } not_an_id;
/* this can crash because NotID doesn't have an ID as its first member */
ID_NAME_SET_AND_VALUE(not_an_id, "name", 10);
}
The issue here is we can't type check the id
argument in the macro against a single type, since it could be an ID
or any struct with an ID
as its first member.
A lot of code I've seen simply casts to the struct all over the place, but it seems it is possible to have a more reliable method.
Is there a way to check at compile time?
Note, for the purpose of this question, we can assume all structs use the same member name for the struct they inherit from.
Note, I was hoping to be able to use something like this...
# define CHECK_TYPE_POLYMORPHIC(val, member, struct_name) \
(void)(_Generic((*(val)), \
/* base-struct */ struct_name: 0, \
/* sub-struct */ default: (_Generic(((val)->member), struct_name: 0))))
/* --- snip --- */
/* check that `var` is an `ID`, or `var->id_base` is */
CHECK_TYPE_POLYMORPHIC(var, id_base, ID);
...but this fails for ID
types in the default
case - because they have no id
member.
So far the only way I found to do this is to type-check against a complete list of all structs, which isn't ideal in some cases (may be many — or defined locally, therefore not known to the macro, see: Compile time check against multiple types in C?).