17 Function-like macros
This chapter covers
- Checking arguments of macros
- Accessing the context of the invocation of a macro
- Working with variadic macros
- Providing default arguments to function calls
We have encountered function-like macros explicitly (see subsection 10.2.1) and implicitly. Some interfaces in the C standard library are typically implemented by using them, such as the type-generic interfaces in <tgmath.h>
. We also have seen that function-like macros can easily obfuscate our code and require a certain restrictive set of rules. The easiest strategy to avoid many of the problems that come with function-like macros is to use them only where they are irreplaceable, and use appropriate means where they are replaceable.
That is, in situations where we have a fixed number of arguments with a known type, we should provide a proper type-safe interface in the form of a function prototype. Let us suppose we have a simple function with side effects:
unsigned count(void) { static counter = 0; ++counter; return counter; }
Now consider that this function is used with a macro to square a value:
#define square_macro(X) (X*X) // Bad: do not use this. ... unsigned a = count(); unsigned b = square_macro(count());
Here, the use of square_macro(count())
is replaced by count()*count()
, two executions of count
.[Exercise 1] That is probably not what a naive reader expects at that point.