Checking function calls are not passed as arguments (C macros)

c, c-preprocessor

Solution

I did find this trick in the MirOS C Preprocessor manual in the section on duplicating side effects:

 #define min(X, Y)                \
 ({ typeof (X) x_ = (X);          \
    typeof (Y) y_ = (Y);          \
    (x_ < y_) ? x_ : y_; })

Problem

In a project I work on we have some utility macros which reference their argument more then once. Lets use a simple example: ``` #define ABS(a) ( (a) < 0 ? (-(a)) : (a) ) ``` Now this is a large code base, and while we do review code, every now and then I find a function call being passed to a macro. This isn't an error, but it means the function call is made multiple times which I normally don't want. In this case we can replace with `fabsf`, `fabs`, `abs` for `float`/`double`/`int` at least, but let's assume there isn't always a good built-in replacement and the macro will stay a macro. Example: ``` f = ABS(dot_v3v3(vel, sp)); /* expands into */ f = ( ( dot_v3v3(vel, sp) ) < 0 ? (-( dot_v3v3(vel, sp) )) : ( dot_v3v3(vel, sp) ) ); ``` So my question is: Can function calls used inside a macro be detected (either as warnings or errors)? Partial solution: Here are some things I already checked on... Comparing Pointers This will cause function calls not to compile, but has the drawback that constants like '1' also give errors as well as expressions like (b - c). ``` #define ABS(a) ((void)((&a) == (&a)), ( (a) < 0 ? (-(a)) : (a) )) ``` Note: I found this already quite handy to point out SOME bad macro use, but since it has false-positives, it cant be left in. C11 Generics Using `_Generic`, you can turn C macros into wrappers for inline functions. This means the problem of a function call being invoked multiple times in a macro goes away. ``` #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 */ \ ) ``` This isn't a workable solution yet — we still support compilers which don't support generics.

Original source