プロプロセッサのマクロ展開と ## 演算子 (2)

前回を書いてからすっかり経ってしまったが、C/C++ プリプロセッサのマクロ展開と ## 演算子の話。
まずは、__TEXT がどう解釈されるのかを見てみる:

#define __TEXT(quote) L ## quote

C++ 標準*1 にはこうある:

『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.
というわけで、通常はここで quoteHELLO_WORLD に置換されるはずが、「preceded by a ##」なので quotequote のままにされる。
で、その次はこれ:
『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.
というわけで、以下のように quoteHELLO_WORLD に置換される:
置換前

L ## quote

置換後

L ## HELLO_WORLD

あれっ!? さっきは「preceded by a ##」だから quoteHELLO_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) は、こんな感じで展開されていく:

  1. TEXT(HELLO_WORLD)
    


  2. __TEXT(quote)
    


  3. __TEXT(HELLO_WORLD)
    


  4. __TEXT("Hello, world.")
    


  5. L ## quote
    


  6. L ## "Hello, world."
    


  7. L"Hello, world."
    


プリプロセッサのテクニック本なんかに出てくる「引数として渡されたマクロ呼び出しを強制的に評価するためにマクロ定義は2重にしておく」みたいな記述は、こういことなのである。

*1:ISO/IEC 14882:2003(E) 『Programming languages − C++