UTF-8文字列リテラルをC++20でも(無理矢理)使う
概要
reinterpret_cast
を使ってconst char*
として扱う、というやり方が手っ取り早い.
好みによっては、UDLを使う方法もいいかもしれない.
困りごと
"cpp-yaml":https://github.com/jbeder/yaml-cpp が文字列をUTF-8として扱ってくれるのでUTF-8文字列リテラル(u8
プレフィックス)を使っていたらC++20にしたときにコンパイラがお怒りになってしまいました. (msvc)
const YAML::Node config = YAML::LoadFile("config.yaml");
config[u8"日本語を使いたいときもあるよ"].as<int>(); // C++20 char8_t がある環境だとNG
// ちなみに、こんなことするとアクセスできない(キーを見つけられない)
config["日本語を使いたいときもあるよ"].as<int>(); // 当たり前だけど、cp932はUTF-8じゃないよ
場当たり的な対処
幸いchar
への読み換え(エイリアス)なのでreinterpret_cast
でキャストしてしまえば、期待通り動作します.
config[reinterpret_cast<const char *>(u8"日本語を使いたいときもあるよ")].as<int>();
reinterpret_cast
使い散らすのも気が進まないので、何か良い方法が無いか探してみることに.
P1423R3
char8_t
の下位互換性について書かれた"P1423R3":https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1423r3.html と、文章中にあるコードが"GitHub":https://github.com/tahonermann/char8_t-remediation にありました.
P1423R3 では、u8
プレフィックスがchar8_t
を返すようになるとコードがどのように壊れるかということと、その問題へのアプローチについて書かれています. 書かれているアプローチのうち検討してみたものは以下のものです.
char8_tを無効化する
コンパイルオプションで無効化してしまう方法. UTF-8しか無い、という環境での開発ならいいかもしれない... けど、そんな環境にいないのと、コードを長期的にメンテするにあたって首を閉めそうなので見送り.
オーバーロードの追加
自分たちで書いている部分に関しては、これで良さそう. ただ、今回は外部のライブラリを使うときに必要なので見送り.
UDLでcharにしてしまう(そしてマクロで包む)
UTF-8な文字列リテラルに対するUDLを実装する方法.
// UDLの実装は、以下を参照してください.
// https://github.com/tahonermann/char8_t-remediation/blob/master/char8_t-remediation.h
config[u8"日本語を使いたいときもあるよ"_as_char].as<int>();
// マクロを使うとこんな感じ
#if defined(__cpp_char8_t)
#define U8(x) u8##x##_as_char
#else
#define U8(x) u8##x
config[U8"日本語を使いたいときもあるよ"_as_char].as<int>();
いい感じ.
結局
githubにあるコードを移植して手元の環境で動くことまで確認したものの、reinterpret_cast
+マクロで対応することにしました.
(UDLがnamespaceくっつけたまんま使えたら良かったんだけど)
Discussion