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 見てたらわかった方法

コンパイルオプションを gnu++11 にすることで、gnu の独自拡張が適用されてトライグラフがデフォルトで無効になる。独自拡張に関しては、stackoverflow とか C++ Extensions - Using the GNU Compiler Collection (GCC) ここを参考に。

まとめ

これから -std=gnu++11 にしよう… 他の言語でも似たような問題を作れるんでしょうか。Java だとユニコードを利用してできるのかな?