Variadic macro in the C preprocessor: Difference between revisions

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search
__VA_OPT__ should be introduced as being both part of C++20 and C23
imported>Lunovaric
No edit summary
 
Line 2: Line 2:
A '''variadic macro''' is a feature of some computer [[programming language]]s, especially the [[C preprocessor]], whereby a [[macro (computer science)|macro]] may be declared to accept a varying number of [[parameter (computer programming)|arguments]].  
A '''variadic macro''' is a feature of some computer [[programming language]]s, especially the [[C preprocessor]], whereby a [[macro (computer science)|macro]] may be declared to accept a varying number of [[parameter (computer programming)|arguments]].  


Variable-argument macros were introduced in 1999 in the ''ISO/IEC 9899:1999'' ([[C99]]) revision of the [[C (programming language)|C]] language standard, and in 2011 in ''ISO/IEC 14882:2011'' ([[C++11]]) revision of the [[C++]] language standard.<ref>Working draft changes for C99 preprocessor synchronization – http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm</ref> Support for variadic macros with no arguments was added in [[C++20]] and will be added in [[C23 (C standard revision)|C23]].<ref>{{cite web|url=https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0306r3.html|title=Comma omission and comma deletion|date=June 18, 2017|access-date=December 24, 2022}}</ref><ref name=N3033/><!-- The first source is the C++ version and the second the C version. They are not duplicates. -->
Variable-argument macros were introduced in 1999 in the ''ISO/IEC 9899:1999'' ([[C99]]) revision of the [[C (programming language)|C]] language standard, and in 2011 in ''ISO/IEC 14882:2011'' ([[C++11]]) revision of the [[C++]] language standard.<ref>{{Cite web |last=Nelson |first=Clark |date=16 July 2004 |title=Working draft changes for C99 preprocessor synchronization |url=http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm |access-date=2025-10-30 |website=www.open-std.org}}</ref> Support for variadic macros with no arguments was added in [[C++20]] and [[C23 (C standard revision)|C23]].<ref>{{cite web|url=https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0306r3.html|title=Comma omission and comma deletion|date=June 18, 2017|access-date=December 24, 2022}}</ref><ref name=N3033/><!-- The first source is the C++ version and the second the C version. They are not duplicates. -->


== Declaration syntax ==
== Declaration syntax ==
Line 23: Line 23:


== Support ==
== Support ==
Several [[compiler]]s support variable-argument macros when compiling C and C++ code: the [[GNU Compiler Collection]] 3.0,<ref name="gcc" /> [[Clang]] (all versions),<ref>Clang source code change that mentions __VA_ARGS__ support (2006-07-29), note that Clang was open-sourced in 2007. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770</ref> [[Visual Studio 2005]],<ref name="msvc" /> [[C++Builder]] 2006, and [[Oracle Solaris Studio]] (formerly Sun Studio) Forte Developer 6 update 2 (C++ version 5.3).<ref>Sun Studio feature comparison – http://developers.sun.com/sunstudio/support/CCcompare.html</ref>  GCC also supports such macros when compiling [[Objective-C]].
Several [[compiler]]s support variable-argument macros when compiling C and C++ code: the [[GNU Compiler Collection]] 3.0,<ref name="gcc" /> [[Clang]] (all versions),<ref>Clang source code change that mentions __VA_ARGS__ support (2006-07-29), note that Clang was open-sourced in 2007. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770{{Dead link|date=July 2025 |bot=InternetArchiveBot |fix-attempted=yes }}</ref> [[Visual Studio 2005]],<ref name="msvc" /> [[C++Builder]] 2006, and [[Oracle Solaris Studio]] (formerly Sun Studio) Forte Developer 6 update 2 (C++ version 5.3).<ref>Sun Studio feature comparison – http://developers.sun.com/sunstudio/support/CCcompare.html</ref>  GCC also supports such macros when compiling [[Objective-C]].


Support for the {{var|__VA_OPT__}} macro to support zero arguments has been added in [[GNU Compiler Collection]] 8,<ref>{{cite web|url=https://gcc.gnu.org/projects/cxx-status.html#cxx2a|title=C++2a Support in GCC|access-date=June 14, 2018}}</ref> [[Clang]] 6,<ref>{{cite web|url=https://clang.llvm.org/cxx_status.html|title=C++ Support in Clang|access-date=June 14, 2018}}</ref> and [[Visual Studio 2019]].<ref>{{cite web|url=https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview|title=MSVC new preprocessor overview|date=September 10, 2020|access-date=December 8, 2020}}</ref>
Support for the {{var|__VA_OPT__}} macro to support zero arguments has been added in [[GNU Compiler Collection]] 8,<ref>{{cite web|url=https://gcc.gnu.org/projects/cxx-status.html#cxx2a|title=C++2a Support in GCC|access-date=June 14, 2018}}</ref> [[Clang]] 6,<ref>{{cite web|url=https://clang.llvm.org/cxx_status.html|title=C++ Support in Clang|access-date=June 14, 2018}}</ref> and [[Visual Studio 2019]].<ref>{{cite web|url=https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview|title=MSVC new preprocessor overview|date=September 10, 2020|access-date=December 8, 2020}}</ref>
Line 30: Line 30:
If a <code>[[printf]]</code>-like [[subroutine|function]] {{code|dbgprintf()}} were desired, which would take the file and line number from which it was called as arguments, the following solution applies.
If a <code>[[printf]]</code>-like [[subroutine|function]] {{code|dbgprintf()}} were desired, which would take the file and line number from which it was called as arguments, the following solution applies.
<!-- Don't change the example back to dprintf; it is a POSIX function: http://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html -->
<!-- Don't change the example back to dprintf; it is a POSIX function: http://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html -->
{{olist
 
|Our implemented function
Our implemented function:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
void realdbgprintf (const char *SourceFilename,
void realdbgprintf(const char* fileName, int line, const char* fmt, ...);
                    int SourceLineno,
                    const char *CFormatString,
                    ...);
</syntaxhighlight>
</syntaxhighlight>
| <p>Due to limitations of the variadic macro support in C++11 the following
 
straightforward solution can fail and should thus be avoided:</p>
Due to limitations of the variadic macro support in C++11 the following straightforward solution can fail and should thus be avoided:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
  #define dbgprintf(cformat, ...) \
#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat, __VA_ARGS__)
    realdbgprintf (__FILE__, __LINE__, cformat, __VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
The reason is that
 
The reason is that  
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
  dbgprintf("Hallo")
dbgprintf("Hallo")
</syntaxhighlight>
</syntaxhighlight>  
gets expanded to
 
gets expanded to  
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
  realdbgprintf (__FILE__, __LINE__, "Hallo", )
realdbgprintf(__FILE__, __LINE__, "Hallo", )
</syntaxhighlight>
</syntaxhighlight>  
 
where the comma before the closing brace will result in a syntax error.
where the comma before the closing brace will result in a syntax error.
| GNU C++ supports a non-portable extension which solves this.
 
GNU C++ supports a non-portable extension which solves this:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
  #define dbgprintf(cformat, ...) \
#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat, ##__VA_ARGS__)
    realdbgprintf (__FILE__, __LINE__, cformat, ##__VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
| C++20 eventually supports the following syntax.
 
C++20 supports the following syntax.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
  #define dbgprintf(cformat, ...) \
#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)
    realdbgprintf (__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
| By using the 'cformat' string as part of the variadic arguments we can
 
circumvent the abovementioned incompatibilities. This is tricky but
By using the <code>cformat</code> string as part of the variadic arguments we can circumvent the abovementioned incompatibilities. This is tricky but portable.
portable.
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
#define dbgprintf(...) realdbgprintf (__FILE__, __LINE__, __VA_ARGS__)
#define dbgprintf(...) realdbgprintf(__FILE__, __LINE__, __VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
}}
 
{{code|dbgprintf()}} could then be called as
{{code|dbgprintf()}} could then be called as  
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
dbgprintf ("Hello, world");
dbgprintf("Hello, world");
</syntaxhighlight>
</syntaxhighlight>  
which expands to
 
which expands to  
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
realdbgprintf (__FILE__, __LINE__, "Hello, world");
realdbgprintf(__FILE__, __LINE__, "Hello, world");
</syntaxhighlight>
</syntaxhighlight>


Another example is
Another example is:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
dbgprintf("%d + %d = %d", 2, 2, 5);
dbgprintf("%d + %d = %d", 2, 2, 5);
</syntaxhighlight>
</syntaxhighlight>
which expands to
which expands to
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
Line 94: Line 94:


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
# define MYLOG(FormatLiteral, ...)  fprintf (stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)
#define MYLOG(FormatLiteral, ...)  fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
The following application works
The following application works
Line 102: Line 102:
which expands to
which expands to
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
fprintf (stderr, "%s(%u): " "Too many balloons %u" "\n", __FILE__, __LINE__, 42);
fprintf(stderr, "%s(%u): " "Too many balloons %u" "\n", __FILE__, __LINE__, 42);
</syntaxhighlight>
</syntaxhighlight>
which is equivalent to
which is equivalent to
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
fprintf (stderr, "%s(%u): Too many balloons %u\n", __FILE__, __LINE__, 42);
fprintf(stderr, "%s(%u): Too many balloons %u\n", __FILE__, __LINE__, 42);
</syntaxhighlight>
</syntaxhighlight>
But look at this application:
But look at this application:
Line 114: Line 114:
which expands to
which expands to
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
fprintf (stderr, "%s(%u): " "Attention!" "\n", __FILE__, __LINE__, );
fprintf(stderr, "%s(%u): " "Attention!" "\n", __FILE__, __LINE__, );
</syntaxhighlight>
</syntaxhighlight>
which generates a syntax error with GCC.
which generates a syntax error with GCC.
Line 120: Line 120:
GCC supports the following (non-portable) extension:
GCC supports the following (non-portable) extension:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
# define MYLOG(FormatLiteral, ...)  fprintf (stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define MYLOG(FormatLiteral, ...)  fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)
</syntaxhighlight>
</syntaxhighlight>
which removes the trailing comma when <code>{{var|__VA_ARGS__}}</code> is empty.
which removes the trailing comma when <code>{{var|__VA_ARGS__}}</code> is empty.
Line 134: Line 134:
{{code|dbgprintf()}} could then be called as:
{{code|dbgprintf()}} could then be called as:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
dbgprintf (("Hello, world %d", 27));
dbgprintf(("Hello, world %d", 27));
</syntaxhighlight>
</syntaxhighlight>
which expands to:
which expands to:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
realdbgprintf ("Hello, world %d", 27);
realdbgprintf("Hello, world %d", 27);
</syntaxhighlight>
</syntaxhighlight>


Line 147: Line 147:
* [[Variadic function]]
* [[Variadic function]]
* [[Variadic template]]
* [[Variadic template]]
{{CProLang}}
{{C++ programming language}}


[[Category:C (programming language)]]
[[Category:C (programming language)]]

Latest revision as of 11:29, 30 October 2025

Template:Short description A variadic macro is a feature of some computer programming languages, especially the C preprocessor, whereby a macro may be declared to accept a varying number of arguments.

Variable-argument macros were introduced in 1999 in the ISO/IEC 9899:1999 (C99) revision of the C language standard, and in 2011 in ISO/IEC 14882:2011 (C++11) revision of the C++ language standard.[1] Support for variadic macros with no arguments was added in C++20 and C23.[2][3]

Declaration syntax

The declaration syntax is similar to that of variadic functions: a sequence of three full stops "..." is used to indicate that one or more arguments must be passed. During macro expansion each occurrence of the special identifier __VA_ARGS__ in the macro replacement list is replaced by the passed arguments.

Additionally, regular macro arguments may be listed before the ...,[4] but regular arguments may not be listed after the ....

No means is provided to access individual arguments in the variable argument list, nor to find out how many were passed. However, macros can be written to count the number of arguments that have been passed.[5]

Both the C99 and C++11 standards require at least one argument, but since C++20 and C23 this limitation has been lifted through the __VA_OPT__ functional macro. The __VA_OPT__ macro is replaced by its argument when arguments are present, and omitted otherwise. Common compilers also permit passing zero arguments before this addition, however.[4][6]

The C preprocessor rules prevent macro names in the argument of __VA_OPT__ from expanding recursively. It is possible to work around this limitation up to an arbitrary fixed number of recursive expansions, however.[7]

Support

Several compilers support variable-argument macros when compiling C and C++ code: the GNU Compiler Collection 3.0,[4] Clang (all versions),[8] Visual Studio 2005,[6] C++Builder 2006, and Oracle Solaris Studio (formerly Sun Studio) Forte Developer 6 update 2 (C++ version 5.3).[9] GCC also supports such macros when compiling Objective-C.

Support for the __VA_OPT__ macro to support zero arguments has been added in GNU Compiler Collection 8,[10] Clang 6,[11] and Visual Studio 2019.[12]

Example

If a printf-like function dbgprintf() were desired, which would take the file and line number from which it was called as arguments, the following solution applies.

Our implemented function:

void realdbgprintf(const char* fileName, int line, const char* fmt, ...);

Due to limitations of the variadic macro support in C++11 the following straightforward solution can fail and should thus be avoided:

#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat, __VA_ARGS__)

The reason is that

dbgprintf("Hallo")

gets expanded to

realdbgprintf(__FILE__, __LINE__, "Hallo", )

where the comma before the closing brace will result in a syntax error.

GNU C++ supports a non-portable extension which solves this:

#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat, ##__VA_ARGS__)

C++20 supports the following syntax.

#define dbgprintf(cformat, ...) realdbgprintf(__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)

By using the cformat string as part of the variadic arguments we can circumvent the abovementioned incompatibilities. This is tricky but portable.

#define dbgprintf(...) realdbgprintf(__FILE__, __LINE__, __VA_ARGS__)

dbgprintf() could then be called as

dbgprintf("Hello, world");

which expands to

realdbgprintf(__FILE__, __LINE__, "Hello, world");

Another example is:

dbgprintf("%d + %d = %d", 2, 2, 5);

which expands to

realdbgprintf(__FILE__, __LINE__, "%d + %d = %d", 2, 2, 5);

Without variadic macros, writing wrappers to printf is not directly possible. The standard workaround is to use the stdargs functionality of C/C++, and have the function call vprintf instead.

Trailing comma

There is a portability issue with generating a trailing comma with empty args for variadic macros in C99. Some compilers (e.g., Visual Studio when not using the new standard-conformant preprocessor[6]) will silently eliminate the trailing comma. Other compilers (e.g.: GCC[4]) support putting ## in front of __VA_ARGS__.

#define MYLOG(FormatLiteral, ...)  fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)

The following application works

MYLOG("Too many balloons %u", 42);

which expands to

fprintf(stderr, "%s(%u): " "Too many balloons %u" "\n", __FILE__, __LINE__, 42);

which is equivalent to

fprintf(stderr, "%s(%u): Too many balloons %u\n", __FILE__, __LINE__, 42);

But look at this application:

MYLOG("Attention!");

which expands to

fprintf(stderr, "%s(%u): " "Attention!" "\n", __FILE__, __LINE__, );

which generates a syntax error with GCC.

GCC supports the following (non-portable) extension:

#define MYLOG(FormatLiteral, ...)  fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)

which removes the trailing comma when __VA_ARGS__ is empty.

C23 solves this problem by introducing __VA_OPT__ like C++.[3]

Alternatives

Before the existence of variable-arguments in C99, it was quite common to use doubly nested parentheses to exploit the variable number of arguments that could be supplied to the printf() function:

#define dbgprintf(x) realdbgprintf x

dbgprintf() could then be called as:

dbgprintf(("Hello, world %d", 27));

which expands to:

realdbgprintf("Hello, world %d", 27);

References

<templatestyles src="Reflist/styles.css" />

  1. ↑ Script error: No such module "citation/CS1".
  2. ↑ Script error: No such module "citation/CS1".
  3. ↑ a b Script error: No such module "citation/CS1".
  4. ↑ a b c d Variadic Macros – Using the GNU Compiler Collection (GCC)
  5. ↑ Script error: No such module "citation/CS1".
  6. ↑ a b c Variadic Macros (C++)
  7. ↑ Recursive macros with C++20 __VA_OPT__
  8. ↑ Clang source code change that mentions __VA_ARGS__ support (2006-07-29), note that Clang was open-sourced in 2007. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770Script error: No such module "Unsubst".
  9. ↑ Sun Studio feature comparison – http://developers.sun.com/sunstudio/support/CCcompare.html
  10. ↑ Script error: No such module "citation/CS1".
  11. ↑ Script error: No such module "citation/CS1".
  12. ↑ Script error: No such module "citation/CS1".

Script error: No such module "Check for unknown parameters".

See also

Template:CProLang Template:C++ programming language

ja:可変長引数