В проекте, над которым я работаю, есть некоторые макросы утилиты, которые ссылаются на их аргумент более одного раза.
Давайте рассмотрим простой пример:
#define ABS(a) ( (a) < 0 ? (-(a)) : (a) )
Теперь это большая база кода, и, хотя мы просматриваем код, время от времени я нахожу вызов функции, передаваемый макросу. Это не ошибка , но это означает, что вызов функции выполняется несколько раз, что я обычно не хочу.
В этом случае мы можем заменить fabsf
, fabs
, abs
на float
/double
/int
по крайней мере, но предположим, что не всегда есть хорошая встроенная замена и макрос останется макросом.
Пример:
f = ABS(dot_v3v3(vel, sp));
/* expands into */
f = ( ( dot_v3v3(vel, sp) ) < 0 ? (-( dot_v3v3(vel, sp) )) : ( dot_v3v3(vel, sp) ) );
Итак, мой вопрос:
Можно ли обнаружить функциональные вызовы, используемые внутри макроса (как предупреждения или ошибки)?
Частичное решение:
Вот некоторые вещи, которые я уже проверил...
Сравнение указателей
Это вызовет вызовы функций не для компиляции, но имеет недостаток, что константы типа "1" также дают ошибки, а также выражения типа (b - c).
#define ABS(a) ((void)((&a) == (&a)), ( (a) < 0 ? (-(a)) : (a) ))
Примечание. Я обнаружил, что это уже довольно удобно указывать на некорректное использование макросов, но поскольку оно имеет ложные срабатывания, оно не может быть оставлено.
C11 Generics
Используя _Generic
, вы можете превратить макросы C в оболочки для встроенных функций. Это означает, что проблема вызова функции, вызываемой несколько раз в макросе, исчезает.
#define ABS(a) \
_Generic((a), \
long double: my_abs_double(a), \
float: my_abs_float(a), \
int: my_abs_int(a) \
/* ... and so on, char, long, short... etc */ \
)
Это еще не выполнимое решение - мы по-прежнему поддерживаем компиляторы, которые не поддерживают дженерики.