Проверка типов в макросе C

Я пытаюсь выполнить проверку типа во время компиляции в одном из моих макросов. Упрощенный код выглядит примерно так:

У меня есть структура, состоящая из:

  • int, представляющий тип функционального интерфейса и
  • общий указатель функции
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.

Технически не разрешается приводить указатели функций к указателям на данные в переносимых программах. Это включает void*

tstanisl 18.02.2023 18:57

Не существует пустых указателей на функции. C различает указатели на функции и указатели на объекты, а void * — это указатель на объект. Вы можете преобразовывать между различными типами указателей функций, но не (напрямую) между указателями функций и указателями объектов.

John Bollinger 18.02.2023 19:04

Тип указателя на функцию void, принимающую параметр int, — void *(fp)(int). Указатель void *p — это указатель данных, а не указатель функции.

David R Tribble 18.02.2023 19:16
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
Запуск PHP на IIS без использования программы установки веб-платформы
Запуск PHP на IIS без использования программы установки веб-платформы
Установщик веб-платформы, предлагаемый компанией Microsoft, перестанет работать 31 декабря 2022 года. Его закрытие привело к тому, что мы не можем...
Оптимизация React Context шаг за шагом в 4 примерах
Оптимизация React Context шаг за шагом в 4 примерах
При использовании компонентов React в сочетании с Context вы можете оптимизировать рендеринг, обернув ваш компонент React в React.memo сразу после...
0
3
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Макросы в 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 Но теперь у меня должны быть правильные ключевые слова. Большое спасибо.

dse 18.02.2023 19:54

Другие вопросы по теме