One more difference between gcc preprocessor and that of MS VS cl. Consider the following snippet:
# define A(x) L ## x
# define B A("b")
# define C(x) x
C(A("a" B))
For 'gcc -E' we get the following:
L"a" A("b")
For 'cl /E' the output is different:
L"a" L"b"
MS preprocessor somehow performs an additional macro expansion. Algorithm of its work is obviously different from that of gcc, but this algorithm also seems to be a secret. Does anyone know how the observed difference can be explained and what is the scheme of preprocessing in MS cl?
GCC is correct. The standard specifies:
C99 6.10.3.4/2 (and also C++98/11 16.3.4/2): If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced.
So, when expanding A("a" B)
, we first replace B
to give A("a" A("B"))
.
A("B")
is not replaced, according to the quoted rule, so the final result is L"a" A("B")
.
Mike's answer is correct, but he actually elides the critical part of the standard that shows why this is so:
6.10.3.4/2 If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Furthermore, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
Note the last clause here that I've emphasized.
So both gcc and MSVC expand the macro A("a" B)
to L"a" A("b")
, but the interesting case (where MSVC screws up) is when the macro is wrapped by the C
macro.
When expanding the C
macro, its argument is first examined for macros to expand and A
is expanded. This is then substituted into the body of C, and then that body is then scanned AGAIN for macros to replace. Now you might think that since this is the expansion of C
, only the name C
will be skipped, but this last clause means that the tokens from the expansion of A
will also skip reexpansions of A.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With