Проверка вызовов функций не передается в качестве аргументов (макросы C)

В проекте, над которым я работаю, есть некоторые макросы утилиты, которые ссылаются на их аргумент более одного раза.

Давайте рассмотрим простой пример:

#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 */ \
        )

Это еще не выполнимое решение - мы по-прежнему поддерживаем компиляторы, которые не поддерживают дженерики.