こんにちは、高木です。
前回はTcl_Obj型の最小限の使い方、そしてラッパークラスについて解説しました。そこでも触れたように、それだけでは何の役にも立ちません。今回はTcl_Obj型のオブジェクトに文字列を格納することにします。
Tclで使う文字列には大きく分けて2種類があります。ひとつはchar型のナル終端文字列であり、もうひとつはTcl_UniChar型のナル終端文字列です。前者はUTF-8、後者はUTF-16をエンコーディングに使っています。
もともとTclのC言語APIは、その名の通りC言語向けのものです。それもかなり古いC90を前提にしています。C++でラッパークラスを作るにあたって、古いライブラリーの形をそのまま踏襲する必要はありません。
今回は、UTF-8の文字列にはchar8_t型の配列を、UTF-16の文字列にはchar16_t型の配列を使うことにします。ただし、char型やwchar_t型の配列が使えないのは不便なので、何らかの方法を考えていきましょう。char32_t型の配列についてもです。
とはいえ、たった1回の記事でそれだけを全部書き切るのは無理があります。少しずつ改定って、最終的にはすべてを網羅できるようにするつもりです。
今回は、char8_t型の文字列からtcl::objクラスのオブジェクトを生成する変換コンストラクターを作ります。また、std::u8string_viewクラスのオブジェクトを返すget_u8string_viewメンバー関数も作ります。これでUTF-8文字列との相互変換ができるようになります。
あと、補助的なメンバー関数として、Tcl_Obj型へのポインターを返すgetメンバー関数も用意しましょう。内部リソースを晒すのはあまりよくないのですが、既存のC言語APIや将来的なC言語APIを呼び出す上ではどうしても必要になります。
今回追加する部分だけを抜粋したコードを以下に貼っておきます。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
obj(const char8_t* s, std::size_t n = -1) : obj_(Tcl_NewStringObj(reinterpret_cast<const char*>(s), static_cast<int>(n))) { Tcl_IncrRefCount(this->obj_); } std::u8string_view get_u8string_view() const { int n; auto s = reinterpret_cast<const char8_t*>(Tcl_GetStringFromObj(this->obj_, &n)); return { s, s + n }; } Tcl_Obj* get() const { return this->obj_; } |
reinterpret_castが多いのでどうしても横長になってしまいますが、まあこれは仕方がありません。また、size_t型からint型にキャストしている部分は、厳密なことをいえばオーバーフローが発生するので問題があるのですが、ここは目をつぶることにしましょう。
同じようにしてUTF-16文字列のためのメンバー関数も追加していきます。
0 1 2 3 4 5 6 7 8 9 10 11 12 |
obj(const char16_t* s, std::size_t n = -1) : obj_(Tcl_NewUnicodeObj(reinterpret_cast<const Tcl_UniChar*>(s), static_cast<int>(n))) { Tcl_IncrRefCount(this->obj_); } std::u16string_view get_u16string_view() const { int n; auto s = reinterpret_cast<const char16_t*>(Tcl_GetUnicodeFromObj(this->obj_, &n)); return { s, s + n }; } |
ラッパークラスを作るのはこういった地味な作業の繰り返しになりますが、しっかり形にしていきたいと思います。
次回は数値からTcl_Obj型のオブジェクトを生成していくことにしましょう。