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.

17.1 How function-like macros work

17.2 Argument checking

17.3 Accessing the context of invocation

17.4 Variable-length argument lists

17.4.1 Variadic macros

17.4.2 A detour: Variadic functions

17.5 Default arguments

Summary