大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

C++でC風ライブラリを作る(UTF-16からUTF-8への変換編)

著者:高木信尚
公開日:2019/04/01
最終更新日:2019/04/01
カテゴリー:技術情報

高木です。こんばんは。

前回はUTF-32からUTF-8への変換ということでcttomb関数を作りました。
しかし、この関数を他の文字型、例えばchar16_t型などで多重定義しようとすると、ひとつの値だけで文字が完結しないケースがあります。
異体字セレクタなんかは別としても、サロゲートペアぐらいは何とか処理したいものです。

そこで、標準Cライブラリのwcrtomb関数に対応するctrtomb関数を作ってはどうかと考えました。
ところが、wcrtomb関数が変換処理を継続できるのは、ISO-2022-JPのようなシフトシーケンスを処理できるということではないかと思います。
その意味ではちょっと違うのかもしれませんが、可能な限り頑張ってみたいと思います。

ctrtomb関数の仕様を決める前に、wcrtomb関数についておさらいすることにしましょう。
wcrtomb関数の関数原型は次のようになっています。

この関数は結構複雑で、それぞれの引数に空ポインタやナル文字を指定することができ、さらにその組み合わせによっても振る舞いが変わります。

順序は前後しますが、もっとも簡単なところから見ていきます。
3番目の引数psに空ポインタを指定した場合は内部的な匿名のオブジェクトが指定されたものとして扱われます。

1番目の引数sが空ポインタではなく、2番目の引数wcがナル文字(L'\0')の場合には、sが指す配列に状態*psを初期状態に戻すためのシフトシーケンスが格納され、次いでナル文字が格納されます。

sが空ポインタの場合はwcは無視されます。
そして、内部的な匿名の配列bufを用いて次のような呼び出しを行ったときと同じように振る舞います。

もっとも典型的な使い方は、sが空ポインタではなく、wcもナル文字ではない場合です。
この場合は、ワイド文字wcを現在のロケールに基づいて多バイト文字に変換した結果をsが指す配列に書き込みます。
さらに、シフト状態を*psに書き込みます。

wcrtomb関数の返却値はsに書き込まれたバイト数です。
sが空ポインタの場合も、内部的な匿名の配列に書き込まれるバイト数が返されます。
エラーが発生した場合は(size_t)-1が返されます。

これを踏まえて、ctrtomb関数の仕様を決めていくことにします。

その前に、標準Cライブラリのmbstate_t型はオブジェクト型であることしか規定されていないため、これを流用するのは困難です。
ここは独自のmbstate_tを定義することにしましょう。

std::uint32_t型に定義しておけば、char32_t型の値もそっくりそのまま保持できますので、とりあえずこうしておきましょう。

さて、いよいよctrtomb関数です。
まずは関数原型を決めます。

3番目の引数psが空ポインタの場合は、内部の匿名オブジェクトが使われるところはwcrtomb関数と同じでよいでしょう。
1番目の引数sが空ポインタの場合の振る舞いも、内部の匿名の配列bufを用いて、

と同じで問題ないはずです。

問題なのは、*psに何を格納すべきかです。
今回は、ucの値をそのまま*psに格納することにします。
こうすることで、ucがサロゲートペアの前半だった場合には、次に呼び出されたときに適切なサロゲートペアになるかどうかを判定できます。
サロゲートペアが成立しない場合や値が不正な場合には例外を送出することでエラーを検出できます。

ucがナル文字の場合、*psにサロゲートペアの前半部が格納されていれば、強引に後半部を出力してもいいのですが、潔く例外を送出してしまったほうがいいでしょう。
とくに問題がないなら、sが指す配列にはナル文字を格納することにします。

これで実装可能なところまで仕様は固まったと思います。
ちょっと長くなりすぎましたので、実装については次回に回すことにします。

    上に戻る