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

