trigraph
2014 TCO Algorithm Round 2B で トライグラフ (trigraph) に軽くハマった。以前にもハマったことがあるので、早めに気づいて致命傷にはならずにすんだけど、スコアをいくらか落としてしまったので対応策をまとめた。
trigraph とは
一部の記号が無いキーボードでも C のプログラムが書けるように、ISO 646 で規定されたより一般的な 3 文字を使って表記する方法。プリプロセッサによって変換される。
トライグラフ - Wikipedia
英語版 wikipedia には 2 文字表記の digraph についても説明がある
Digraphs and trigraphs - Wikipedia
このため、次のようなソースコードは大部分の人の意図と異なる出力となる
#include <iostream> using namespace std; int main() { std::cout << "??-" << std::endl; return 0; }
出力は
~
の一文字になってしまっている。 C++11での実行結果
外部ファイルを読み込んだ場合は変換されないので、プログラミングコンテスト系ではソースコードに直接テストケースを記述する topcoder ぐらいでしか発生しない問題かも。
コンパイルオプション
ここで、先ほどのコードのコンパイラを変えてみる。先ほどは C++11 でコンパイルしたが、今度は C++4.8.1 でコンパイル、実行してみる。
C++4.8.1での実行結果
今度は変換されずに出力されている。まだ調べきれていないけど、C++11 ではデフォルトで trigraph を変換するようだ。なので、今回 Topcoder でハマった人はだいたい g++ でコンパイルして、オプションに次の指定をしていたと思われる
g++ -std=c++11 ...
この場合は次のような警告が出ていたはずだ。以下の警告は (GCC) 4.9.0 で試した結果。
SwitchingGame.cpp:111:4: warning: trigraph ??- converted to ~ [-Wtrigraphs] "-??-?+"}
trigraph ??- が変換されたことがわかる。ソースコードが C++11 の機能を使っていないのであれば、 -std=c++11 を外すと、次のような警告になる。
SwitchingGame.cpp:111:4: warning: trigraph ??- ignored, use -trigraphs to enable [-Wtrigraphs] "-??-?+"}
trigraph は無視されたので、変換したいなら -trigraphs をつけてと言われる。こっちのほうが望ましい挙動なんだけど…
トライグラフを有効にするオプションがあるなら、無効にするオプションがあるのではないかとも思うけど、どうも見つからない(´・ω・`)。ちなみに、-Wno-trigraphs とすると警告を無効にできる。でも -no-trigraphs オプションは存在しない。何故。
解決策
2 番目がおすすめ?
1. トライグラフを書かないようにする
"??-" となっていたら、"\?\?-" と、? にバックスラッシュを追加すればトライグラフにならない。"??""-" と分割して書いてもよい。ソースコードを適当に置換すればよい。 https://ideone.com/yqQfbq
2. コンパイルオプションを -std=gnu++11 にする
twitter 見てたらわかった方法
手元の環境,-std=c++11 だとtrigraphは変換されて,-std=gnu++11 だとtrigraphは警告付きで無視されるらしい
— くりんぺっと (@climpet) 2014, 6月 7
コンパイルオプションを gnu++11 にすることで、gnu の独自拡張が適用されてトライグラフがデフォルトで無効になる。独自拡張に関しては、stackoverflow とか C++ Extensions - Using the GNU Compiler Collection (GCC) ここを参考に。