Я пытаюсь выполнить проверку типа во время компиляции в одном из моих макросов. Упрощенный код выглядит примерно так:
У меня есть структура, состоящая из:
typedef void(*generic_func_t)(void);
typedef struct
{
int type;
generic_func_t function;
} func_def_t;
Для инициализации этой структуры я определил два макроса;
#define INIT_FUNC1_TYPE(function) {1, (generic_func_t)function}
#define INIT_FUNC2_TYPE(function) {2, (generic_func_t)function}
Использование будет примерно таким:
int(*func1_t)(int, int, int);
int(*func2_t)(int, int);
int foo(int a, int b)
{
return a + b;
}
func_def_t active_func = INIT_FUNC2_TYPE(foo);
...
int a = 2;
int b = 3;
int c = 4;
int d;
if (active_func.type == 1)
{
func1_t func = (func1_t)active_func.function;
d = func(a, b, c);
}
if (active_func.type == 2)
{
func2_t func = (func2_t)active_func.function;
d = func(a, b);
}
Моя проблема сейчас в том, что любой тип функции может быть приведен к (void*).
Но должна быть возможность спроектировать макрос таким образом, чтобы компилятор принимал только определенный тип функционального интерфейса. Может с typeof?
Допустим, есть типы функций:
void(*func1_t)(int, int, int);
int(*func2_t)(float, int);
Как мне это сделать?
Отредактировано: Я изменил указатель с void* на generic_func_t, но суть вопросов осталась прежней. Макрос INIT_FUNC1_TYPE должен принимать только функции с таким интерфейсом, как func1_t. Макрос INIT_FUNC2_TYPE должен принимать только функции с таким интерфейсом, как func2_t.
Не существует пустых указателей на функции. C различает указатели на функции и указатели на объекты, а void * — это указатель на объект. Вы можете преобразовывать между различными типами указателей функций, но не (напрямую) между указателями функций и указателями объектов.
Тип указателя на функцию void, принимающую параметр int, — void *(fp)(int). Указатель void *p — это указатель данных, а не указатель функции.
Макросы в C представляют собой простые замены текста и полностью игнорируют типы C. Если вы хотите проверить тип, вам придется объявить член структуры как указатель на функцию, например
typedef struct
{
int type;
void *(function) (int,int,int);
} func_def_t;
У меня есть структура, состоящая из: int, представляющий тип функционального интерфейса и пустой указатель на функцию
Нет, потому что void * указывает на объект, а не на функцию. Однако вы можете свободно преобразовывать (путем приведения типов) типы указателей на функции, поэтому вы можете выбрать любой добросовестный тип указателя на функцию для своих целей. Возможно void (*)(void):
typedef struct {
int type;
void (*function)(void);
} func_def_t;
Вам нужно будет преобразовать обратно в правильный тип указателя функции, чтобы вызвать функцию в любом случае, так что это не сложнее в использовании, чем void *.
Моя проблема сейчас в том, что любой тип функции может быть приведен к (void*).
Ну, ни один тип указателя функции не может быть приведен к void * строго соответствующей программой. Но если ваш компилятор принимает такие преобразования как расширение, то да, по-видимому, он работает для любого типа указателя функции. То же самое можно сказать и о преобразовании из любого типа указателя функции в любой другой тип указателя функции, в том числе в строго соответствующих программах.
Но должна быть возможность спроектировать макрос таким образом, чтобы компилятор принимал только определенный тип функционального интерфейса. Может с typeof?
Стандартный C не имеет typeof, хотя я полагаю, что он запланирован для включения в C23. Но я не думаю, что это поможет вам.
Однако вы можете использовать в своем макросе универсальное выражение, чтобы ограничить его определенными типами указателей функций. На самом деле, это может позволить вам иметь один макрос вместо двух.
Пример для каждого типа:
typedef void (func)(void);
typedef struct {
int type;
func *function;
} func_def_t;
#define INIT_FUNC1_TYPE(f) {1, _Generic((f), void (*)(int, int, int) : (func *) f) }
#define INIT_FUNC2_TYPE(f) {2, _Generic((f), int (*)(float, int) : (func *) f) }
Компилятор будет возражать, если один из этих макросов вызывается с аргументом, который не соответствует одному из имен типов в _Generic выборе внутри, и каждый из них предоставляет ровно один вариант.
Но это упускает возможность сделать все с помощью одного макроса. Учти это:
#define INIT_FUNC_TYPE(f) { \
.type = _Generic((f), \
void (*)(int, int, int) : 1, \
int (*)(float, int) : 2), \
.function = (func *) f \
}
Теперь (один) макрос выбирает ключ функционального типа на основе типа аргумента макроса (и компилятор отклоняет аргументы, которые не соответствуют ни одному из указанных типов). Это лучше масштабируется, чем подход, требующий отдельного макроса для каждой поддерживаемой сигнатуры функции.
Это именно то, что я искал. Работает как шарм. Теперь я пытаюсь понять, что именно вы там делали :D Но теперь у меня должны быть правильные ключевые слова. Большое спасибо.
Технически не разрешается приводить указатели функций к указателям на данные в переносимых программах. Это включает void*