2014年08月27日
C/C++プリプロセッサの挙動の違い(/ ## /編)
マニアックなネタが続いて申し訳ありません。またプリプロセッサの話です。
前回:C/C++プリプロセッサの挙動の違い(defined編)
前々回:米Facebook社のC/C++プリプロセッサWarpをWindows環境でビルド
Warp の挙動を調べていて、以下のような、マクロ展開後にコメントが生成されるようなコードが通らない事に気が付きました。
(元は MinGW GCC の x86_64-gdcproject-mingw32/x86_64-gdcproject-mingw32/sysroot/mingw/include/wtypes.h ヘッダ)
前回:C/C++プリプロセッサの挙動の違い(defined編)
前々回:米Facebook社のC/C++プリプロセッサWarpをWindows環境でビルド
Warp の挙動を調べていて、以下のような、マクロ展開後にコメントが生成されるようなコードが通らない事に気が付きました。
(元は MinGW GCC の x86_64-gdcproject-mingw32/x86_64-gdcproject-mingw32/sysroot/mingw/include/wtypes.h ヘッダ)
$ cat gen_cpp_comment.c #define _VARIANT_BOOL /##/ $ ./warp.exe --stdout gen_cpp_comment.c # 1 "gen_cpp_comment.c" # 1 "i:\test\warp//" # 1 "<command-line>" # 1 "gen_cpp_comment.c" gen_cpp_comment.c(2) : ## cannot appear at end of macro text
実はこれ自体は Warp のバグ※なのですが、GNU cpp (GCC) と VC++ の間でも挙動が違いました。
※ skipWhitespace() というテンプレート関数の中で、'*' や '/' が 1 文字の場合に取りこぼしてしまっているようです。(そのため、最後の '/' がスキップされ「マクロ文字列の最後で ## は使用できない」とエラーになっています。)どうやら Warp は D 言語の InputRange という抽象的な構造のテンプレートで全体が記述されているので、入力ストリームの 1 文字先読みが困難(InputRange に可能な操作は empty/front/popFront() のみ)なため、トリッキーなコードになっているのがエンバグの原因に思えます。
GCC 4.9.0 では、定義自体は通りますが(そのため wtype.h ヘッダは問題無く使用できる)、実際に識別子を使用するとエラーになります。
N1256(C99 final draft)の 6.4.9 Comments には、根拠はちょっとわからなかったのですが、
http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
もし詳しい方がおられましたら、ご教示いただければ幸いです。
関連しそうな URL: stackoverflow: Does the C preprocessor strip comments or expand macros first?
※ skipWhitespace() というテンプレート関数の中で、'*' や '/' が 1 文字の場合に取りこぼしてしまっているようです。(そのため、最後の '/' がスキップされ「マクロ文字列の最後で ## は使用できない」とエラーになっています。)どうやら Warp は D 言語の InputRange という抽象的な構造のテンプレートで全体が記述されているので、入力ストリームの 1 文字先読みが困難(InputRange に可能な操作は empty/front/popFront() のみ)なため、トリッキーなコードになっているのがエンバグの原因に思えます。
GCC 4.9.0 では、定義自体は通りますが(そのため wtype.h ヘッダは問題無く使用できる)、実際に識別子を使用するとエラーになります。
$ cat gen_cpp_comment.c #define _VARIANT_BOOL /##/ _VARIANT_BOOL $ gcc -E gen_cpp_comment.c # 1 "gen_cpp_comment.c" # 1 "VC++ では通ります。" # 1 " " # 1 "gen_cpp_comment.c" gen_cpp_comment.c:1:23: error: pasting "/" and "/" does not give a valid preprocessing token #define _VARIANT_BOOL /##/ ^ gen_cpp_comment.c:3:1: note: in expansion of macro '_VARIANT_BOOL' _VARIANT_BOOL ^ / /
>cl /E gen_cpp_comment.c Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. gen_cpp_comment.c #line 1 "gen_cpp_comment.c"いずれにせよ避けるべき悪い書き方なのですが、このようなコメントの生成が valid なのかどうかは、ちょっと明確にはわかりませんでした。
N1256(C99 final draft)の 6.4.9 Comments には、根拠はちょっとわからなかったのですが、
http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
#define glue(x,y) x##yという近い例があり、また、6.10.3.3 The ## operator に
glue(/,/) k(); // syntax error, not comment
If the result is not a valid preprocessing token, the behavior is undefined.とありますが、// がプリプロセッサのトークンとして valid なのかどうかがよくわかりませんでした。
もし詳しい方がおられましたら、ご教示いただければ幸いです。
関連しそうな URL: stackoverflow: Does the C preprocessor strip comments or expand macros first?