スポンサーリンク

warning: multi-line commentの解決方法 ~DxLibをcmakeでリンクしようとすると警告が出た話~

記事内に広告が含まれています。

前回、cmakeを使ってビルドしているプログラムにDxLibをリンクする方法を解説しました。

ですが人によっては、もしかしたらコンパイル中に、warning: multi-line commentという警告が出たかもしれません(少なくとも、僕の環境ではその警告が出ました)。もしかしたら、僕以外にも同じ現象に出会った方がいらっしゃるかもしれません。

他にも、DxLibを利用しているわけではないけど、その警告が出た方もいらっしゃるかもしれません。

このmulti-line commentという警告は、文字コードのミスマッチによって発生しています(どのようなミスマッチなのかは後述しますので、ここではとりあえず「文字コード関係の問題で警告が出たんだな」という程度に理解しておいてください)。

この警告は文字コードに起因するものなので、DxLibを使っているか否かはあまり関係ありません。なのでDxLibは、この記事の中では話を分かりやすくするための具体例として出てくると思います。

ですから、別にDxLibでの警告を修正したいわけではないという方にも役に立つ内容もあると思います。

スポンサーリンク

Windows 11で内部で使用されるコードを変更する方法

先ほど冒頭で、「警告が出たかもしれません」と説明しました。それは、DxLibをリンクするときにwarning: multi-line commentが出なかった可能性もあるということでもあります。その理由は、僕のプログラミング環境にあります。

僕はWindows 11を使っていて、(2023/03/25現在)ベータ版※1の機能ではありますが、システム内部で使われる文字コードをUTF-8に変更※2できるようになっています(機能名が分からないのですが、後述する手順で利用できるこの機能は、僕が勝手に「システム内部で使われる文字コードをUTF-8に変更する機能」だと解釈しているだけなので、間違っているかもしれません)。

※1. すでにご存じの方も多いでしょうが、ベータ版とは大雑把には、「まだまだ完成ではないけど、使えないというわけではないから、興味があったら試してみてね」というようなバージョンのことです。ベータ版は動作が不安定(ある特定の条件では仕様通りの動きをしなかったり、クラッシュしたり)だとされています。それに、正式公開版では使用感が変わっていることもあります。

※2. この機能を利用する方法も後述します。

そして僕は、どんな不具合が出るのか興味があるので、システム内部で使われる文字コードをUTF-8に設定しています(この設定は、multi-line commentという警告とは、もしかしたら関係ないのかもしれません)。

内部の文字コードをいじっているからこのエラーが出たのか、それとも何か別なことが原因でそのようなエラーが出たのかは分かりません。

Windows 11をお使いの方であれば、設定画面を開いて、「時刻と言語」→「言語と地域」→「管理用の言語の設定」とクリックしていくと、ポップアップ画面(というか、ダイアログ画面というか)が開きます。

もしくは、「Windowsロゴキー」と「rキー」の同時押しで表示される画面にintl.cplと入力してEnterを押せば出てくるウィンドウを、「管理タブ」に変更しても同様の画面になります。

そこに、「システム ロケールの変更」というボタンがありますから、それをクリックすると、さらにポップアップ画面が表示されます。その画面に「ベータ: ワールドワイド言語サポートでUnicode UTF-8を使用」というチェックボックスが配置されていますから、それをチェックします。

すると、再起動しろと言われるので、再起動すると、内部で使われている文字コードがUTF-8に変更されます。

この設定が関係あったのかどうかは分かりませんが、この環境でDxLibをリンクしようとすると、僕の環境では「multi-line comment」という警告が発生しました。

ということで次の節で、まずは「multi-line comment」がどのような警告なのかを解説していきます。

コンピュータはファイルをどう扱っているか?

さっそく警告を説明したいところですが、それを説明するには、コンピュータ内部の動きを簡単に理解しておかなければならないので、先にコンピュータがファイルをどのように扱っているかを説明します。

「文字コード」や「ダメ文字」と聞いて、「あぁ、こういうことが起こったんだろうな」と予想できた方は、次の節まで飛ばしていただいて問題ありません。

コンピュータは0か1かで表現されるビットパターンを扱うことで、こうして皆さんの画面に文字を表示できているわけです。

ファイルを保存したり、読み出したりするときも同様に、0と1で構成されたビットパターンを扱っています。コンピュータは、0と1で構成されたビットパターンをSSDやHDDなどの特定の場所に書き込んだり、あるいは読み出したりすることで、人間に「ファイルを保存する」や「ファイルを表示する」といった機能を提供しています。

しかし、同じ01の並びであっても、それを解釈する規則によって、ソフトウェアの動作が変わってくることがあるのです。

例えば、ある規則では0001というビットパターンが「イ」という文字に対応しているけど、別の規則では、同じ0001というビットパターンが「ロ」という文字に対応していると考えてください。

その場合、同じようにSSDに0001というビットパターンが記録されていても、読み出すときに前者の規則を使えば「イ」という文字が読み出されることになりますが、後者の規則を使うと「ロ」という文字が読み出されることになります。

つまり人間視点では、保存した文字(「イ」)とは別の文字(「ロ」)が読み出されることになるわけですね。このようなことが他の文字でも起こると、訳の分からない文章になってしまいます。場合によっては、ビットパターンに対応する文字が無いかもしれません。

これはコンピュータがファイルを読み出すときの話でしたが、ファイルを記録するときも同様です。「イ」という文字を記録したとしましょう。前者の規則で記録したら0001というビットパターンが記録されますが、後者の規則で記録したら0001以外のビットパターンが記録されることになります(例えば0002など)。

これらの「ビットパターンと文字を対応させる規則」は「文字コード」と呼ばれています。この文字コード(つまり対応規則)には、いくつもの種類があって、その種類の一つとして、SHIFT-JISやUTF-8というものがあります。

たまにSHIFT-JISやUTF-8という言葉を聞いた方もいらっしゃるかもしれませんが、それらは文字コード、つまりは、0と1で表現されたビットパターンと文字とを対応付けた規則のことなのですね。

つまり、記録するときはSHIFT-JISを使ったけど、読み出すときはUTF-8を使ったような場合は、文字化けが起こることになります。ですから、データを記録するときに使った文字コード(対応規則)と読み出すときの文字コードは同じでなければ、人間が保存したデータを、保存した通りに表示することができません。

スポンサーリンク

文字コードの齟齬

今回DxLibで使われていた文字コードはSHIFT-JISと呼ばれている文字コードでした。しかし、僕はC++コンパイラにclangを使っています。このコンパイラの公式マニュアルを見てみると、次のようなことが書かれています。

  /execution-charset:<value>
                          Runtime encoding, supports only UTF-8
  /source-charset:<value> Source encoding, supports only UTF-8
  /utf-8                  Set source and runtime encoding to UTF-8 (default)
Execute clang-cl /? to see a list of supported options:(Command-Line Options)以下から抜粋

要するに「clangはUTF-8しかサポートしない」ということです。これは、SHIFT-JISで記録されたようなファイルはclangに与えられないし、与えたとしても、(ファイルの文字コードに関わらず)それがUTF-8として処理されるということになります。

分かりやすくまとめると、次のような齟齬が起きることになります。

  1. DxLib(記録方式): SHIFT-JIS
  2. clang(読み出し方式): UTF-8

SHIFT-JISで記録されているものをUTF-8という方式で読み取ることになるため、(前節で「イ」と「ロ」の例で説明したように、)「DxLibには、本来はこの文字が記録されてるのに、clangが別の文字だと認識しちゃった」というようなことが起こります。

ここで、その話は一旦置いておいて、ひとまずC++に用意されている言語規則を確認しておきます。ここで確認しておきたい規則とは、「行の終端に’\’を入れると、その次の行も同一の行として扱う」という規則のことです。どのような意味か、次のコードで説明します。

// (1) ここはコメント
ここはコメントでない

// (2) ここはコメント \
ここもコメント

この(1)の方は、コメントが最終行までで、次の行に書かれている「ここはコメントではない」はコメントとしては認識されません。しかし、(2)は、最後に’\’記号があります。この場合、その次の行に書かれた「ここもコメント」はコメントとして認識されます。

つまり、’\’を書けば、一つの行に書くべきコードを、複数行に渡って書けるようになるということですね。特に#defineマクロを書くときは、よく使います。

この’\’という記号は、UTF-8では0x5cというビットパターンと対応付けられています(0x5cがビットパターンに見えない方もいるかもしれませんが、これは16進数表記になっているために、ビットパターンに見えていないだけです)。

そして、SHIFT-JISには、0x5cで終わるような文字がいくつもあります。それらの文字は、UTF-8として読み取られると、'<何かよく分からん文字>”\’という記号の列として読み取られることになります。例えば「表」という文字などは、0x5cで終わる文字の代表例です。次のコードを見てください。

// 購買記録表
for( row_index = 0; row_index < row_num; ++row_index ) {
  std::cout << "product_name: " << archive[row_index][0] << std::endl;
}

このコードがUTF-8で保存されていれば、普通にコンパイルできます。しかし、このコードがSHIFT-JISで保存されていて、それをUTF-8で読み取った場合、「表」という文字が0x5cで終わるため、’\’として認識されることになります。つまり、clangは上記のコードを次のように認識することになります。

// 購買記録<何かよく分からん文字>\
for( row_index = 0; row_index < row_num; ++row_index ) {
  std::cout << "product_name: " << archive[row_index][0] << std::endl;
}

実際には、「購買記録」の部分も<何かよく分からん文字>の羅列だと認識されていると思います。ですが、ここで重要なのは「表」という文字が0x5cで終わっていることなので、「購買記録」の部分はそのままとしました。

まとめると、SHIFT-JISでは「表」と対応付けられているビットパターンに対して、次のような対応付けがなされているわけですな。

  • SHITF-JIS : 表
  • UTF-8 : <何かよく分からん変な文字><改行>

結果として、コメントの最終文字が意図せず0x5cで終わることになって、その次の行もコメントとして認識されることになります(そして、コンパイルエラーとなります)。このような場合、clangではwarning: multi-line commentが発生します。

しかし、具体的な発生条件は分かりません(元がSHIFT-JISだと判別できたときだけなのか、それとも(文字コードに関わらず、)コメントアウトしている行の最後に’\’があったらなのか等々)。

このようにSHIFT-JISでは最後が0x5cで終わるようになっている文字のことを、ダメ文字(正式な名称ではないそうですが)と呼びます。ダメ文字に関する詳しい解説はこちらに、そして一覧表はこちらにまとめられていました。

ということで、先ほどのwarning: multi-line commentの原因は文字コードの齟齬でした。ならば、記録方式(文字コード)を変更してしまえばいいんじゃなかろうかと考えるのは自然な成り行きでしょう。ということで、次の節で文字コードを変換する方法を解説します。

スポンサーリンク

文字コードを変換するだけなんだけど・・・

僕の場合は、msys2を利用しているので、以下のようなbashスクリプトを書いてヘッダファイルの文字コードを変更しました。msys2ではiconvというコマンドを利用できるので、そのコマンドを利用します(まぁ、僕はこうしたというだけで、皆さんも同じようにする必要はありませんが)。

#!/bin/bash

# カレントディレクトリ以下のすべてのファイルを取得する
# ここで取得したファイルを、すべて指定したディレクトリ以下にコピーしたい
FILE_ARRAY=`/c/msys64/usr/bin/find.exe ./ -name '.h'`

for file_path in $FILE_ARRAY; do
  distination=./utf8/${file_path##./}
  dist_directory=$(dirname $distination)

  if [ ! -d ${dist_directory} ]; then
    mkdir -p ${dist_directory}
  fi

  iconv -f SJIS -t UTF-8 $file_path > $distination
done

このスクリプトを、「DxLib_GCC\プロジェクトに追加すべきファイル_GCC(MinGW)用\12_1_0」(要するに、使いたいヘッダとライブラリが保存されているディレクトリ)に保存して実行しました。すると、同ディレクトリにutf8というディレクトリができて、文字コードをUTF-8に変更されたファイルが出来上がります。

このスクリプトの${file_path##./}という部分は、左端からそのパターン(ここでは./)に当てはまるまでを除外しています。これは、findコマンドの戻り値が./***という形式になっているために、普通に./utf8/の後につなげてしまうと、./utf8/./***となってしまうからです。

別にそれでもいいような気はするのですが、なんとなく気持ち悪かったので、findコマンドの戻り値から./を除外するようにしています。

そして、utf8というディレクトリが存在するかどうかをifで判定しています。ここでは、./utf8/というディレクトリがあるかどうかを調べたいので、distination(文字コード変換後のファイル名./utf8/*.h)から、ディレクトリ部分だけを切り出したもの(要するに、./utf8)を判定しています。

そして、そのディレクトリが無いときに限って、新しく./utf8というディレクトリをmkdirで作っています。

その後、本命のiconvコマンドによって文字コードを変換しています。このコマンドは標準出力に変換結果を出力するようになっているので、その出力先をリダイレクト>によって./utf8/*.hというファイルに変更しています。

しかし僕の環境では、このスクリプトを実行すると、半角のバックスラッシュが、円記号となってしまいました。

つまり、バックスラッシュが、UTF-8で表記すると「0xc2 0xa5」で表現される円マークに変換されていました。

これは、コマンドは正常だけど、僕の環境の影響でこうなったのか、それともコマンドの方にバグがあるのか、そもそもこの動作は仕様なのか、等々分からないことが多数です。なので、このような現象が皆さんの手元でも起こっているとは限りません。

そして、そのような文字があるとエラーとなるので、その円マークを0x5cで表現される’\’に変更する必要がありました。この現象がなぜ起こっているのか分からない上に、そんなに数も多くなかったので、手作業で変更しました。

なので、文字コードを変換しても、「円マークがあるぞ」とコンパイラに言われたら、「もしかしたらバックスラッシュが円マークに置き換えられたのではないかな」と疑って、変更してみるといいかもしれません。

まとめ

気を付けること(というか、僕の環境で起こったこと)と修正方法としては次の2点です。

  1. warning: multi-lineが出た
    → iconv(Linuxコマンド)で文字コードを変更した
  2. バックスラッシュが円マークに置き換えられていた
    → 目で見て修正、もしくはbashスクリプトを組む(でも、目で見て修正した方が良いような気がする)

そして、warning: multi-line commentは文字コードのミスマッチによって起こっているのでした(だから文字コードを変更しようということになったのでした)。

しかし、(少なくとも僕の環境では、)文字コードを変更する過程で、バックスラッシュ0x5cが円マーク0xc2 0xa5に置き換えられました。そのときは、文字コードの変換に加えて、円マークをバックスラッシュに変更するという修正も必要なのでした。

しかし、bashスクリプトを作って、文字コードの変換と円マークからバックスラッシュへの置換という2つの作業を、自動化することもできると思います。

ただ個人的には、今回の話は環境次第な部分が多すぎると思うので、一つ一つ手作業で確認した方が良いような気はしています。円マークを置き換えるという作業だって、ほんの数個ですし。

文字コードについて詳しく知りたい方は、最近読んだ以下の文献が分かりやすかったです。

タイトルとURLをコピーしました