Полиморфные структуры довольно распространены в C, но часто включают явное приведение типов, позволяющее случайно преобразовать несовместимые структуры.
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);
}
Проблема здесь в том, что мы не можем проверить тип аргумента id
в макросе на соответствие одному типу, так как это может быть ID
или любая структура с ID
в качестве первого члена.
Много кода, который я видел, просто везде приводит к структуре, но кажется, что можно иметь более надежный метод.
Есть ли способ проверить во время компиляции?
Обратите внимание, что для целей этого вопроса мы можем предположить, что все структуры используют одно и то же имя члена для структуры, от которой они наследуются.
Заметьте, я надеялся, что смогу использовать что-то подобное...
# 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);
... но это не работает для типов ID
в случае default
, потому что у них нет члена id
.
Пока что единственный способ, который я нашел для этого, — это проверка типов по полному списку всех структур, что в некоторых случаях не идеально (их может быть много — или они определены локально, поэтому не известны макросу, см.: Проверка времени компиляции для нескольких типов в C?).