プロプロセッサのマクロ展開と ## 演算子 (2)
前回を書いてからすっかり経ってしまったが、C/C++ プリプロセッサのマクロ展開と ##
演算子の話。
まずは、__TEXT
がどう解釈されるのかを見てみる:
#define __TEXT(quote) L ## quote
- 『16.3 Macro replacement』の「9」
-
A preprocessing directive of the form
# define identifier lparen identifier-listopt ) replacement-list new-line
defines a function-like macro with parameters, similar syntactically to a function call. The parameters are specified by the optional list of identifiers, whose scope extends from their declaration in the identifier list until the new-line character that terminates the#define
preprocessing directive. Each subsequent instance of the function-like macro name followed by a(
as the next preprocessing token introduces the sequence of preprocessing tokens that is replaced by the replacement list in the definition (an invocation of the macro). The replaced sequence of preprocessing tokens is terminated by the matching)
preprocessing token, skipping intervening matched pairs of left and right parenthesis preprocessing tokens. Within the sequence of preprocessing tokens making up an invocation of a function-like macro, new-line is considered a normal white-space character.
- 展開前
-
__TEXT(HELLO_WORLD)
- 展開後
-
L ## quote
- 『16.3.1 Argument substitution』
-
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a
#
or##
preprocessing token or followed by a##
preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the translation unit; no other preprocessing tokens are available.
quote
が HELLO_WORLD
に置換されるはずが、「preceded by a ##
」なので quote
は quote
のままにされる。で、その次はこれ:
- 『16.3.3 The ## operator』の「2」
-
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.
quote
が HELLO_WORLD
に置換される:- 置換前
-
L ## quote
- 置換後
-
L ## HELLO_WORLD
##
」だから quote
は HELLO_WORLD
に置換されないって言ったくせに、結局 HELLO_WORLD
になっちゃったよ?? いやいや、同じではない。さっきは「replaced by the corresponding argument after all macros contained therein have been expanded」だったが、今度は「replaced by the corresponding argument’s preprocessing token sequence」としか書かれていない。そう、ここでは、マクロ HELLO_WORLD
はマクロ展開を受けずにプリプロセッサトークンのままになるのだ。そして、最後にこれがくる:
- 『16.3.3 The ## operator』の「3」
-
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. If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement. The order of evaluation of##
operators is unspecified.
##
の処理前-
L ## HELLO_WORLD
##
の処理後-
LHELLO_WORLD
LHELLO_WORL
こそが、このエラーメッセージの元凶だ:
(11) : error C2065: 'LHELLO_WORLD' : 定義されていない識別子です。
では、最後に復習を。ちゃんとコンパイルされる方のマクロ呼び出しである TEXT(HELLO_WORLD)
は、こんな感じで展開されていく:
-
TEXT(HELLO_WORLD)
-
__TEXT(quote)
-
__TEXT(HELLO_WORLD)
-
__TEXT("Hello, world.")
-
L ## quote
-
L ## "Hello, world."
-
L"Hello, world."