JTC1/SC22/WG14
N976
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 <control-line>:
# 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("<h1>" format "</h1>\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.