liveBook · Manning

Variadic macro

As explained in Modern C

A variadic macro is a C function-like macro whose parameter list accepts a variable number of arguments through an ellipsis.

Pass a variable number of values to a trace

#include <stdio.h> #define TRACE(F, ...) fprintf(stderr, F, __VA_ARGS__) int main(void) { int x = 2; TRACE("x=%d, next=%d\n", x, x + 1); return 0; } The call prints `x=2, next=3`. `F` receives the format string; `__VA_ARGS__` supplies the remaining arguments to `fprintf`. section 16.5.1

A variadic macro is a function-like macro whose parameter list ends in `...`. The ellipsis accepts a variable-length argument list, and `__VA_ARGS__` names that list in the replacement. Unlike the example above, a call with no arguments after `F` cannot use the same replacement: it leaves `fprintf` without an argument after the final comma. The format string must still match the values passed to `fprintf`. section 16.5.1

TAKEAWAY: Use a function-like macro when its variable-length interface does work an ordinary fixed-argument function cannot do; otherwise prefer an inline function. Macro replacement can repeat an argument expression and therefore repeat its side effects. section 16.0

138    ** @brief Traces with or without values 
139    **
140    ** This implementation has the particularity of adding a format 
141    ** @c "%.0d" to skip the last element of the list, which was 
142    ** artificially added. 
143    **/
144   #define TRACE_PRINT8(...)                        \
145   TRACE_PRINT6(TRACE_FIRST(__VA_ARGS__) "%.0d",    \
146                TRACE_LAST(__VA_ARGS__))


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":3}],[{\"line\":0,\"ch\":29},{\"line\":0,\"ch\":31}],[{\"line\":2,\"ch\":65},{\"line\":2,\"ch\":67}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":3}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":3}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":3}],[{\"line\":3,\"ch\":20},{\"line\":3,\"ch\":22}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":3}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":3}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":3}],[{\"line\":6,\"ch\":6},{\"line\":6,\"ch\":13}],[{\"line\":7,\"ch\":0},{\"line\":7,\"ch\":3}],[{\"line\":7,\"ch\":31},{\"line\":7,\"ch\":42}],[{\"line\":8,\"ch\":30},{\"line\":8,\"ch\":41}],[{\"line\":8,\"ch\":0},{\"line\":8,\"ch\":3}],[{\"line\":7,\"ch\":31},{\"line\":7,\"ch\":42}],[{\"line\":8,\"ch\":30},{\"line\":8,\"ch\":41}]]"}
!@%STYLE%@!

This trace macro accepts either a format string alone or a format string followed by values. Helper macros separate the first argument from the rest and append an artificial zero; `%.0d` consumes that zero without printing it. section 16.5.1

Other uses of the argument list:

  • Stringify it with `#__VA_ARGS__` to print the written expressions, including their separating commas, alongside their values. section 16.5.1
  • Count arguments for a trace interface, then initialize a `long double` array from the values. The array initializer converts its elements to the target type; this counting macro supports up to 31 elements and treats an empty list as one empty argument. section 16.5.1 section 16.5.2

Do not confuse a variadic macro with a variadic function such as `printf`. Both use `...`, but a function receives arguments whose types must be handled correctly; a macro can instead pass its arguments into an array initializer that converts them to a common type. That distinction does not make a forwarded `fprintf` call type-safe: its format and arguments still need to agree. section 16.5 section 16.5.2

How Modern C covers it

How deep it goes
Works through argument forwarding, empty-list handling, stringification, counting, and array conversion; distinguishes these from variadic-function argument handling.
Examples
Builds tracing macros that print formatted values, accept messages without values, stringify expressions, and pass counted values through an array.
What it assumes
Assumes familiarity with function-like macros and formatted-printing calls; defines the ellipsis and `__VA_ARGS__` in context.
What you can do after
Build it from scratch: write a variadic trace macro and adapt it to handle calls with or without values.
How it connects
Connects function-like macros to inline functions, variadic functions, stringification, and formatted output.

Also explained in

Compare how 2 books explain variadic macro →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C. The section links point to the parts of the book it draws on.