gcc にはソースファイルの入力文字セットを指定するオプション -finput-charset
があります。これは可能な限り使用せずに、ソースファイルの文字セットは UTF-8 にしよう! という話です。
そもそも -finput-charset
って何?
ソースファイルの文字セットを指定するためのオプションです。デフォルトは UTF-8 です。
cp932 (Shift-JIS) でソースが書かれていて、このオプションをつけていないとまれに問題を起こします。例えば、以下のコードは -finput-charset
なしでは n = 0
を出力します(わざとらしい例ですが)。
#include <iostream>
int main() {
int n = 0;
// ここで n に代入する値の意味は、同梱の資料を見てください。
// see: 〇〇設定表
n = 42;
std::cout << "n = " << n << std::endl;
}
昔懐かしい 表
の文字化け問題ですね。cp932 の 表
は 0x95 0x5c というバイト列ですが、0x5c は \
のため、コメント行末尾に \
が来たと解釈されて次の行 n = 42
もコメント扱いされます。
-finput-charset=cp932
をつければ、コンパイラは文字 表
を 1 文字と正しく解釈するので上のコードはちゃんと n = 42
を出力します。
問題が発生する例
問題はここからです。
試しに -finput-charset=cp932
をつけて、以下の 1 行のコードをコンパイルしてみましょう。
#include <forward_list>
このコードはコンパイルが通りません。gcc 8.2.0, 7.3.0, 5.5.0 で確認しましたが全てエラーになりました。
$ echo '#include <forward_list>' > tmp.cpp
$ g++ -finput-charset=cp932 tmp.cpp
In file included from tmp.cpp:1:0:
/usr/include/c++/7/forward_list:38:31: error: failure to convert cp932 to UTF-8
#include <bits/forward_list.h>
^
(...以下略...)
なぜこうなるかというと、gcc の forward_list のヘッダファイル内で UTF-8 文字を使っているためです。
入力文字セットに cp932 を指定したので、gcc は iconv を使ってソースファイルを cp932 から gcc が使用するソース文字セットに変換してコンパイルします。そこで #include <forward_list>
から読み込むヘッダファイル内に cp932 でない文字が含まれていると変換できないのでエラーになっていました。
ちなみに、UTF-8 文字は bits/forward_list.h
の 783 行目にあります。
$ grep -P -n "[^\x00-\x7F]" /usr/include/c++/7/bits/forward_list.h
783: // 23.3.4.5 modifiers:
え、UTF-8 文字なんてない? いえ、よく見てください。fi
は fi という 2 文字ではなくて LATIN SMALL LIGATURE FI (\uFB01) という UTF-8 の 1 文字です。
推測ですが、実装したひとは non-ascii な文字を導入したつもりは全くなくて、pdf か web からコメントの文 23.3.4.5 modifiers
をコピーしてきて混入したのではないかと思われます。
どうするべきか?
-finput-charset
を指定するのはやめて、ソースコードを UTF-8 にしましょう。
STL に限らず、GitHub のかなり人気のライブラリ(例えば catch2 とか)でも同じ問題があり、-finput-charset
を使う限り結構な頻度ででくわします。