Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Standard Behavior Of Function-Like Macro With Empty Argument Next To ## Operator?

Take the following example:

#define FOO(x) bar x ## baz
FOO( )

What is the expected output of the above code, according to the ANSI C and C99 standards, after the preprocessing phase?

I ran the above through gcc -E and clang -E, and both produced output equivalent to the following:

bar baz


Also, if the above preprocessed output is considered to conform to the standards, then what about this?

#define FOO(x) x ## baz
FOO( )

With the above modification GCC and clang still produce output equivalent to the following, without emitting any warning or error (even with the -Wall -Werror flags):

baz


The reason I am suspicious that the above output is not conformant to the standards is that the ANSI C 9899:1990 standard states:

6.8.3.3 The ## operator

A ## preprocessing token shall not occur at the beginning or at the end of a replacement list for either form of macro definition.

If, in the replacement list, a parameter is immediately preceded or followed by a ## preprocessing token, the parameter is replaced by the corresponding argument's preprocessing token sequence.

Okay, so technically the ## operator is NOT at the beginning of the replacement list in both of the above two examples, but x does expand to... a single space? nothing? ...so the ## operator (at least, as I understand it) is concatenating a space pp-token (or nothing) to baz.


Furthermore, the standard states:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.

So, considering the first example I gave above, why shouldn't the correct output be, say, this?

barbaz


UPDATE

As a sidenote, I did manage to get GCC to emit an error for the first example I gave above when preprocessing the code as follows:

gcc -ansi -pedantic -Wall -Werror -E ex1.c -o -

The output (with error) is:

foo.c:2:6: error: invoking macro FOO argument 1: empty macro arguments are undefined in ISO C90 [-Werror=pedantic]
 FOO( )
      ^
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "foo.c"

bar baz
cc1: all warnings being treated as errors

The error, however, is not related to the behavior of the ## operator, which is what this question was all about. It's still interesting nonetheless.

Also interesting to note is that no error is emitted by clang when preprocessing the file, using the same flags.

like image 787
jinscoe123 Avatar asked Nov 20 '18 06:11

jinscoe123


1 Answers

Okay, so technically the ## operator is NOT at the beginning of the replacement list in both of the above two examples,

The word "technically" here is redundant. The replacement list of both of your macros has x before the ##, so that rule doesn't apply.

but x does expand to... a single space? nothing?

6.4 lists what's considered preprocessing tokens: header-name, identifier, pp-number, character-constant, string-literal, punctuator, and "each non-whitespace character that cannot be one of the above". Note what is conspicuously absent from this list... whitespace in and of itself.

So your whitespace in your argument to FOO is not significant; your argument is a sequence of 0 preprocessing tokens (that is, 0 of the things in that list above). That means that this applies:

6.10.3.3 p2:

however, if an argument consists of no preprocessing tokens, the parameter is replaced by a placemarker preprocessing token instead.

The next paragraph, 3, applies to the result here:

concatenation of a placemarker with a non-placemarker preprocessing token results in the non-placemarker preprocessing token.

So, placemarker pasted onto baz produces baz.

like image 186
H Walters Avatar answered Oct 24 '22 12:10

H Walters