Visual C++ 2015および2017 RCでは、<codecvt>の各クラステンプレートにchar16_tやchar32_tを組み合わせるとリンクエラーになるという問題があります。今回、これに対する条件付きのWorkaroundについて書きます。
まずは、エラーになるコードの例を提示します。
#include <codecvt> #include <string> #include <cassert> #include <locale> int main() { std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter_utf32; std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter_utf16; std::string u8str = u8"\U0001F359"; std::u32string u32str = converter_utf32.from_bytes(u8str); std::u16string u16str = converter_utf16.from_bytes(u8str); } |
これをVisual C++ 2015でコンパイル・リンクするとこんなエラーメッセージが出てきます。
u.obj : error LNK2019: 未解決の外部シンボル "public: static class std::locale::id std::codecvt::id" (?id@?$codecvt@_SDU_Mbstatet@@@std@@2V0locale@2@A) が関数 "public: __thiscall std::locale::locale >(class std::locale const &,class std::codecvt_utf8_utf16 const *)" (??$?0V?$codecvt_utf8_utf16@_S$0BAPPPP@$0A@@std@@@locale@std@@QAE@ABV01@PBV?$codecvt_utf8_utf16@_S$0BAPPPP@$0A@@1@@Z) で参照されました。 u.obj : error LNK2019: 未解決の外部シンボル "public: static class std::locale::id std::codecvt ::id" (?id@?$codecvt@_UDU_Mbstatet@@@std@@2V0locale@2@A) が関数 "public: __thiscall std::locale::locale >(class std::locale const &,class std::codecvt_utf8 const *)" (??$?0V?$codecvt_utf8@_U$0BAPPPP@$0A@@std@@@locale@std@@QAE@ABV01@PBV?$codecvt_utf8@_U$0BAPPPP@$0A@@1@@Z) で参照されました。 u.exe : fatal error LNK1120: 2 件の未解決の外部参照
さて、エラーメッセージを読むと、1件目はstd::locale::id型のstaticメンバー変数std::codecvt<char16_t,char,struct _Mbstatet>::idが見つからないという内容です。2件目も同じ内容でchar32_tになっているだけです。ということは、これを自分で定義したら良いのではないでしょうか?
というわけで、こんなコードを書いたらうまくいきました。コンパイル・リンクできてちゃんと実行できました。
#include <codecvt> #include <string> #include <cassert> #include <locale> // この2行を追加 std::locale::id std::codecvt<char32_t, char, std::mbstate_t>::id; std::locale::id std::codecvt<char16_t, char, std::mbstate_t>::id; int main() { std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter_utf32; std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter_utf16; std::string u8str = u8"\U0001F359"; std::u32string u32str = converter_utf32.from_bytes(u8str); std::u16string u16str = converter_utf16.from_bytes(u8str); } |
ただし、この方法を使うには条件があります。それは、VC++ランタイムを静的リンクする場合(/MTや/MTdコンパイルオプション)だけ使えるというものです。/MDや/MDdだと、問題となるstaticメンバー変数idが__declspec(dllimport)
付きの宣言となってしまうため、この方法が使えません。別のリンクエラーになります。
ところで、以下のcpprefjpの各ページのサンプルコードもこの問題に該当します。それぞれのサンプルコードの作成・動作確認にあたり、私はこの方法で乗り切りました。
まとめです。
- codecvt_utf16, codecvt_utf8, codecvt_utf8_utf16のいずれかに、char16_tかchar32_tを使うとリンクエラーが出る。
- そうなったら、codecvtクラステンプレートののstaticメンバー変数idを自分で定義すれば、リンクが通る。
バグ報告は出ているそう(Visual C++における文字コード変換 – C++と色々)なので、将来的にはVisual C++が直ることを期待します。あと/MD, /MDdで使えるWorkaround欲しいです。
スポンサード リンク |
この記事のカテゴリ
- VC++ ⇒ VC++ 2015のcodecvtでリンクエラーになる問題の回避策