プロプロセッサのマクロ展開と ## 演算子 (1)
Windows API には TCHAR
型というものがある((MSDN ライブラリでは TCHAR
は unsigned char
の typedef
だと解説されているが、Visual C++ 2008 Express Edition に付いてきた WinNT.h では char
の typedef
になっていた。)):
#ifdef UNICODE typedef wchar_t TCHAR; #else typedef char TCHAR; #endif
wchar_t
型ではないのでワイド文字としては扱えず、char
でもないので標準 C ライブラリなどの各種 API にも渡せないという扱いづらいやつだが、コードページから Unicode への過渡期には大きな役割を果たしてくれた型である。
さて、この TCHAR
型には TEXT()
マクロという相方がいて、“TCHAR
リテラル”とでも呼ぶべきものを書けるようになっている。すなわち、TEXT("some string")
と書くと、マクロ UNICODE
が define
されているかに応じて以下のいずれかに展開される:
UNICODE
が定義されているL"some string"
UNICODE
が定義されていない"some string"
define
されている。必要なところだけ抜き出してきて整形してある。)):
#ifdef UNICODE # define __TEXT(quote) L ## quote #else # define __TEXT(quote) quote #endif #define TEXT(quote) __TEXT(quote)
キーになるのは、##
という演算子だ。これは、マクロの置換テキストの中でのみ利用可能な演算子で、マクロの実引数を字句レベルで連結するという離れ業をやってのける。これにより、TEXT("some string")
は、__TEXT("some string")
から L ## "some string"
を経て L"some string"
に展開される。
ところで、ここに1つの疑問が湧く。なんで、__TEXT()
マクロを経由しないといけないのだろう? シンプルに「#define TEXT(quote) L ## quote」ではダメなのだろうか?
結論から言うと、このシンプルな解はダメだ。以下の2つをそれぞれコンパイルしてみれば分かる:
#include <windows.h> #include <tchar.h> int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) { MessageBox(NULL, __TEXT("Hello, world."), TEXT("hello"), MB_OK); return 0; }
#include <windows.h> #include <tchar.h> #define HELLO_WORLD "Hello, world." int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) { MessageBox(NULL, __TEXT(HELLO_WORLD), TEXT("hello"), MB_OK); return 0; }
__TEXT()
マクロに渡す "Hello, world." という文字列を直接書くか HELLO_WORLD
というマクロ経由で渡すかということだけだ。ただそれだけの違いで、コンパイルができたりできなかったりする。そして、__TEXT()
ではなく TEXT()
を使えば、HELLO_WORLD
マクロ経由でもコンパイルできるようになるのだ。では、これを切り口にしてプリプロセッサのマクロ展開と
##
演算子の面白い挙動を見ていこう、……と思ったが、長くなってしまったので今日はこの辺りまでで。ちなみに、全てを物語るのは、コンパイラからのこのメッセージだ:
(11) : error C2065: 'LHELLO_WORLD' : 定義されていない識別子です。