ISO/ IEC 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.