こんにちは、高木です。
前回、ジオメトリーマネージャーのサブコマンドであるinfoやslavesに対応するメンバー関数を実装しましたが、それらが結果として返すのはリストでした。今回はそのリストの扱いについて考えていくことにします。
前回も少しだけ触れたように、リストの要素数と要素の列を取得するにはTcl_ListObjGetElements関数を使います。ほかにもTcl_ListObjLength関数を使えば要素数だけを取得できますし、Tcl_ListObjIndex関数を使えば1要素ずつ取り出せるのですが、Tcl_ListObjGetElements関数を常に使っても問題ないと思います。
まずはTcl_ListObjGetElements関数の使い方を簡単に見ていくことにしましょう。
0 1 2 3 4 5 |
tcl::obj list(u8"abc def ghi"); int objc; Tcl_Obj** objv; Tcl_ListObjGetElements(interp.get(), list.get(), &objc, &objv); |
こういう感じで呼び出せば、objcにリストの要素数が、objvにリストの要素の列が格納されます。
objvに格納されるのはあくまでも生のTcl_Obj*の列ですので、tcl::obj型の列に直すには自分でコードを書く必要があります。
ただ、実際には生のTcl_Obj*が必要になるケースの方が多いように思いますので、変にラッパー関数を作らない方がいいのかもしれませんね。
tcl::obj型として要素を取り出したいのであれば、生のTcl_Obj*型からtcl::obj型には暗黙的に変換されますので、取り出した結果をtcl::obj型の変数に代入すればOKです。
ところで、objvに格納されるポインター値ですが、Tcl_Obj型が内部で保持している配列の先頭要素へのポインターなので自分で明示的に解放する必要は内容です。
得られたTcl_Obj*の列を明示的に解放する必要がないのであれば、std::spanを使って前回作ったメンバー関数を次のように書き換えてもいいかもしれません。
0 1 2 3 4 5 6 7 8 9 10 11 12 |
std::span<Tcl_Obj*> info(const window& w) const { if (sub_command(u8"info", w) != TCL_OK) return {}; auto interp = w.get().interpreter().get(); auto r = Tcl_GetObjResult(interp); int objc; Tcl_Obj** objv; Tcl_ListObjGetElements(interp, r, &objc, &objv); return { objv + 0, objv + objc }; } |
結果をtcl::obj型で返すと、それがリストかどうかがパッと見ではわかりにくいので、こっちの方がいいでしょうね。slavesメンバー関数も同様に変更していいと思います。
次回からは個別のジオメトリーマネージャーについて考えていくことにします。まずはpackerから手をつけていく予定です。