こんにちは、高木です。
前回予告したように、今回はTclの変数のラッパークラスについて考えていきます。最初にTclの変数についてざっとおさらいしておきます。
Tclの変数には静的な型がありません。事前に宣言する必要もなく、いきなりsetコマンドを使って変数に値を設定することができます。
0 1 2 |
set foo 123 |
上記のように書けば、変数fooに123という値を設定することができます。変数を参照する際には、[set foo]と書いてもいいのですが、通常は$fooとします。たとえば次のようにすれば変数fooの値を標準出力に書き込むことができます。
0 1 2 |
puts $foo |
Tclでは配列を使うこともできます。配列の添字はC言語のように角括弧を使うのではなく丸括弧を使います。また、添字は整数値だけではなく文字列を使うこともできます。
0 1 2 |
set hello(world) "Hello World" |
上記の例では、helloという配列の要素hello(world)に文字列”Hello World”を設定しています。配列を参照するときも$を付けて$hello(world)とすればOKです。
また、設定済みの変数を削除するにはunsetコマンドを使います。
0 1 2 |
unset foo |
これらを踏まえてTcl変数のラッパークラスを作っていきます。
Tcl変数を操作するためのAPIにも、新しい形式のTcl_Obj型を使うものと古い形式の文字列を使うものがあります。いつものように、今回も新しい形式のものだけを対象にすることにしましょう。ただし、変数を削除するAPIはなぜか古い形式しかないので、やむを得ずそちらを使うことにします。
Tcl変数に値を設定するAPIはTcl_ObjSetVar2関数になります。同じページで他のAPIもすべて解説されています。
値を取得するにはTcl_ObjGetVar2関数を、変数を削除するにはTcl_UnsetVar関数とTcl_UnsetVar2関数を使います。
Tcl_ObjSetVar2関数では、part1Ptrとpart2Ptrという2つの引数で変数名を指定します。普通の変数であればpart2Ptrには空ポインターを指定しますが、配列の場合には添字を指定することになります。
Tcl_ObjGetVar2関数も同様です。Tcl_UnsetVar2関数はTcl_Obj型へのポインターではなく文字列で名前を指定するようになっていますが考え方は同じです。
これらをラップしたクラスは次のようになります。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
class var { public: var(interpreter interp, const obj& part1) : interp_(interp), part1_(part1) { } // 変数名 obj name() const { return this->part1_; } // 値の取得 obj get(int flags = 0) const { return Tcl_ObjGetVar2(this->interp_.get(), this->part1_.get(), nullptr, flags); } obj get(const obj& part2, int flags = 0) const { return Tcl_ObjGetVar2(this->interp_.get(), this->part1_.get(), part2.get(), flags); } // 値の設定 obj set(const obj& value, int flags = 0) const { return Tcl_ObjSetVar2(this->interp_.get(), this->part1_.get(), nullptr, value.get(), flags); } obj set(const obj& value, const obj& part2, int flags = 0) const { return Tcl_ObjSetVar2(this->interp_.get(), this->part1_.get(), part2.get(), value.get(), flags); } // 値の削除 int unset(int flags = 0) { return Tcl_UnsetVar(this->interp_.get(), reinterpret_cast<const char*>(this->part1_.get_u8string_view().data()), flags); } int unset(const obj& part2, int flags = 0) { return Tcl_UnsetVar2(this->interp_.get(), reinterpret_cast<const char*>(this->part1_.get_u8string_view().data()), reinterpret_cast<const char*>(part2.get_u8string_view().data()), flags); } private: interpreter interp_; obj part1_; }; |
ごくごく素直にラップしただけのクラスです。
上記ではvarクラスにinterpreterを包含してしまいましたが、ルートとなるインタープリターを使用する前提であればinterpreterを抱え込まなくてもいいでしょう。別のインタープリターを使うときだけその都度使用するインタープリターを引数で渡すという手もあります。
ただ、変数はインタープリターの中で管理されるものです。名前だけ同じでインタープリターをまたがって使うようなものではありませんので、今回のようにインタープリターを包含してしまうことにしました。
今回はここまでです。次回はなにかウィジェットを扱ってみようと思います。