WG14 Document: N976 Date: 2002-05-17 ------------------------------------------------------------------------------ Submitter: Zachary Weinberg (US) Submission Date: 2001-11-18 Source: Reference Document: Version: 1.0 Date: 2001-11-18 14:56 -0800 Subject: Named rest-argument parameters to variadic macros Summary This DR codifies existing practice, as implemented by the GNU C compiler, which permits the variable arguments of a variadic macro to be referenced from the macro body by a name other than __VA_ARGS__. Details ISO/IEC 9899:1999 permits the definition of a variadic macro, that is, a function-like macro which takes a variable number of arguments. Such macros are defined with the token ... at the end of their parameter list (6.10.3p10), and may substitute the complete list of variable arguments by using the identifier __VA_ARGS__ in the replacement list (6.10.3.1p2). For example, the standard function printf may be defined as a variadic macro: #define printf(...) fprintf(stdout, __VA_ARGS__) Or a more elaborate version of the standard assert() macro might permit a user-specified message in addition to the text of the expression: #define assert_with_message(expr, ...) \ do { if (!(expr)) \ assertion_failed_with_message(__FILE__, __LINE__, __func__, \ #expr, __VA_ARGS__); \ } while (0) In a more complicated variadic macro, the code would often be easier to read and comprehend if the syntax permitted the programmer to use a meaningful name, rather than __VA_ARGS__, to refer to the variable arguments. GNU C supports an extended syntax for variadic macros which allows this. In the extended syntax, an identifier immediately precedes the ... token. That identifier, instead of __VA_ARGS__, is replaced by the variable arguments whenever it appears in the macro replacement list. For example, #define assert_with_message(expr, message...) \ do { if (!(expr)) \ assertion_failed_with_message(__FILE__, __LINE__, __func__, \ #expr, message); \ } while (0) This version makes it clear what the variable arguments actually are: a message, presumably containing printf-style escapes, and the values to substitute into the message. It is anticipated that this change will pose no burden to implementations. In fact, since __VA_ARGS__ ceases to be a special case, it is likely to make implementations simpler. Suggested Technical Corrigendum In 6.10p1, replace these productions for : # define identifier lparen ... ) replacement-list new-line # define identifier lparen identifier-list , ... ) replacement-list new-line with # define identifier lparen identifier-opt ... ) replacement-list new-line # define identifier lparen identifier-list , identifier-opt ... ) replacement-list new-line In 6.10.3p4 and 6.10.3p12, change (excluding the ...) to (excluding the ... and the identifier immediately preceding it, if present) Delete 6.10.3p5. Replace 6.10.3.1p2 with If the parameter list ended with an ..., the identifier immediately preceding it is treated as a parameter, and the variable arguments shall form the preprocessing tokens used to replace it. If that identifier was omitted, the identifier __VA_ARGS__ receives this treatment, as if it had appeared immediately before the ellipsis. In 6.10.3.5p9, replace #define report(test, ...) ((test)?puts(#test):\ printf(__VA_ARGS__)) with #define report(test, failmsg...) ((test)?puts(#test):\ printf(failmsg)) Submitter: Zachary Weinberg (US) Submission Date: 2001-11-18 Source: Reference Document: Version: 1.0 Date: 2001-11-18 15:50 -0800 Subject: Proposed solution for problems with variable arguments to macros Summary This DR proposes the standardization of existing practice, as implemented by the GNU C compiler, which eliminates a class of problems with variable arguments to macros. These problems appear whenever the last non-optional argument to a macro cannot be considered part of the variable argument list. They also appear when programmers wish to write a variadic macro in the same style that they would write variadic functions. Details In the current specification, variadic macros must take at least one variable argument (6.10.3p4). This interferes with writing them in the most natural way. For instance, it is most natural to write a short-hand macro for fprintf(stderr) as follows: #define debug(format, ...) fprintf(stderr, format, __VA_ARGS__) This mirrors the way you would write the same short-hand as a variadic function: inline int debug(const char *format, ...) { ... } However, if you do this, you must always supply at least one extra argument. debug("error code %d\n", errno); // ok debug("input file mangled"); // constraint violation Leaving the extra argument empty - debug("input file mangled", ); will produce a syntax error (6.5.2). It is possible to supply unnecessary arguments to the macro, which fprintf will ignore (7.19.6.1p2) - debug("input file mangled", 0); but this will run foul of a compiler which checks fprintf's format string against its variable argument list. In the examples given by the standard, the problem is sidestepped by merging the format argument into the variable arguments: #define debug(...) fprintf(stderr, __VA_ARGS__) This notation is less transparent than the original, and does not mirror what is done with variadic functions. Also, it only works as long as the last non-optional argument does not need to be separated from the variable arguments. A macro such as #define print_h1(format, ...) \ printf("

" format "

\n", __VA_ARGS__) cannot be written any other way. The GNU C compiler implements two extensions for variadic macros, which eliminate the problem. (1) The constraint that at least one variable argument be provided is lifted. It is acceptable (in phase 4) to write #define debug(format, ...) fprintf(stderr, format, __VA_ARGS__) ... debug("input file mangled\n"); /* expands to */ fprintf(stderr, "input file mangled\n", ); (2) The above example naturally produces a syntax error in phase 7. To avoid this, the semantics of the ## operator are extended: if ## appears between a comma and __VA_ARGS__, and no variable arguments were provided, the comma is deleted from the macro expansion. #define debug(format, ...) fprintf(stderr, format, ##__VA_ARGS__) debug("input file mangled\n"); debug("input file mangled\n", ); debug("input file %s mangled\n", filename); /* expand to */ fprintf(stderr, "input file mangled\n"); fprintf(stderr, "input file mangled\n", ); fprintf(stderr, "input file mangled\n", filename); An alternate possibility is to have the comma-deletion behavior happen for any use of __VA_ARGS__ immediately after a comma. #define debug(format, ...) fprintf(stderr, format, __VA_ARGS__) debug("input file mangled\n"); /* expands to */ fprintf(stderr, "input file mangled\n"); I do not know of any problems with doing it this way. However, I do not know that it is definitely safe. The ## notation has approximately a decade of existing practice behind it, while this alternative does not; therefore I am proposing only the ## notation. Suggested Technical Corrigendum In 6.10.3p4, change Otherwise, there shall be more arguments in the macro invocation ... to Otherwise, there shall be at least as many arguments in the macro invocation ... Add to 6.10.3.1p2 If there were no variable arguments, __VA_ARGS__ shall be replaced by no tokens. In 6.10.3.3p2, change ... preprocessing token sequence; however, if an argument consists of no preprocessing tokens, the parameter is replaced by a placemarker preprocessing token instead.(footnote 145) to ... preprocessing token sequence, with two exceptions. First, if an argument consists of no preprocessing tokens, the parameter is replaced by a placemarker preprocessing token instead.(footnote 145) Second, if ## appears immediately after a comma and before the parameter __VA_ARGS__, the behavior depends on whether or not there were any variable arguments. If there were none, the sequence ", ## __VA_ARGS__" is discarded from the replacement list. Otherwise, the ## is discarded from the replacement list, and __VA_ARGS__ is replaced by the variable arguments after all macros contained therein have been completely expanded, as if the ## had not been present. Optionally, in 6.10.3.3p3, add a footnote to If the result is not a valid preprocessing token, the behavior is undefined. reading The ", ## __VA_ARGS__" notation does not attempt to concatenate any tokens, so its behavior is well-defined even though normally only a placemarker token can be concatenated with a comma.