|
本サイトは移転しました。旧アドレスからのリダイレクトは2025年03月31日(月)まで有効です。
|
🛈 | ✖ |
自作codecvtファセットを用いず標準のままユニコード文字列をコマンドプロンプトへ出力する方法を確認する。
mingw-w64でワイド文字列(ユニコード文字列)をウィンドウズコマンドプロンプトへC++ストリームで標準入出力(wcin/wcout)しても、ASCII文字に限定されそれ以外(例えば日本語)は出力されない。標準入出力がcodecvt<wchar_t,char,mbstate_t>ファセットで文字コードを変換するにはC言語標準入出力の非同期化が必要である事に加え、C++ファイルストリーム一般の問題としてそもそもcodecvtが文字コードを正しく変換する必要がある。デフォルトの標準codecvtにその変換を行わせるには、C++標準ライブラリlocale::globalスタティックメンバ関数でグローバルC++ロケールを設定するのではなく、C言語標準ライブラリsetlocale関数でC言語ロケールを設定しなければならない。本記事はその理由を説明してソースコードを確認する。
本サイトはバージョン1.3.1.Xに至るまでmingw-w64が用意する標準codecvt<wchar_t,char,mbstate_t>がASCIIしか扱えないとしていたが、最近これが誤りである事に気付いた。本サイト以前よりそのように信じて不明は10年に達し、本サイトで公開して汗顔の至りである。本記事の追加と共に関連記事を全面的に書き換えた。
エクスキューズする。C++ストリームは当然C++標準ライブラリの範疇で、まさかC言語標準ライブラリの関数によるC言語ロケール設定が必要とは想像もしなかった。以下が誤解に至った概略である。
以下に新たに得た知見をまとめる。
この問題はC++標準入出力(wcin/wcout)(ただしC言語標準入出力との同期解除が前提)に限らないC++ファイルストリーム(wifstream/wofstream)一般で、wxWidgetsライブラリ利用のデスクトップアプリケーションがファイル入出力をwifstream/wofstreamで行う場合にも影響する。GNU gettextによる国際化機能が利用するwxLocaleクラスはInitメンバ関数からwxLanguageInfo::TrySetLocaleメンバ関数とwxSetlocale関数を経由してsetlocale関数をシステムロケール名でコールする。つまりwxWidgetsライブラリを利用する際、wxLocale国際化機能の有無で標準codecvtによるwifstream/wofstreamがASCII以外を変換できるかどうか、日本語環境であればユニコード文字列からシフトJISへ変換できるかどうかが変わる。
GCCソースはGitHubパブリックミラーで最新バージョンを参照できる。mingw-w64はGCCの一バージョンなのでソースは共通のはずだ。codecvt<wchar_t,char,mbstate_t>はC++標準ライブラリ(libstdc++-v3)に存在する。そのメンバ関数を定義するソースコードファイル(codecvt_members.cc)は--enable-clocaleコンフィグ(GCC 14.1.0 Standard C++ Library Manual 2.2 Configure)に依存してgnu、generic、dragonfly、vxworksの四つが存在する。dragonflyとvxworksは特殊ターゲットで、gnuはGNU供給のC言語標準ライブラリglibcを用いるバージョンで、mingw-w64が用いるのはgenericである。
ソースコードでcodecvt<wchar_t,char,mbstate_t>のメンバ関数定義を確認する。do_outメンバ関数は内部ユニコード文字列を外部マルチバイト文字列に変換するためC言語標準ライブラリwcrtomb関数をコールする。do_inメンバ関数は外部マルチバイト文字列を内部ユニコード文字列に変換するためmbrtowc関数をコールする。
なお本記事の目的にはdo_out/do_inメンバ関数のみで十分であるが、自作codecvtとの比較に供するためフルソースコード(2022年1月3日コミット)を掲示する。