前回はGObject Introspectionでのエラーの扱いについて説明しました。今回は戻り値のオブジェクトの寿命について説明します。と、考えていたのですが、書いていたら、間違って「戻り値の」オブジェクトの寿命ではなく「一般的な」オブジェクトの寿命について説明してしまっていました。。。なので、今回は「一般的な」オブジェクトの寿命についての説明で、「戻り値の」オブジェクトの寿命は次回にします。。。実際に動くバインディングはkou/arrow-glibにあります。
Apache Arrowでのオブジェクトの寿命の管理:std::shared_ptr
Apache Arrowはstd::shared_ptr
でオブジェクトの寿命を管理しています。バインディングは該当オブジェクトを使っているときは参照を増やして、使わなくなったら参照を減らせばよいです。
GObject Introspectionでのオブジェクトの寿命の管理:GObject
GObject Introspection対応ライブラリーはC言語で実装しますが、オブジェクト指向な機能を使えます。たとえば、カプセル化・継承・ポリモルフィズムなどの機能があります。
これらの機能はGObject IntrospectionがベースにしているGObjectというライブラリーが提供しています。GObject Introspectionでのオブジェクトの寿命の管理はこのGObjectの機能を使います。
GObjectではオブジェクトの寿命はリファレンスカウントで管理します。具体的にはGObject
を継承したクラスを作り、そのオブジェクトに対してg_object_ref()
/g_object_unref()
を使うことで寿命を管理します。
Apache ArrowのオブジェクトとGObject
のオブジェクトを適切な寿命でなじませるには次のようにします。
-
Apache Arrowのクラスと
GObject
のクラスを1対1で対応させる。(例:arrow::Array
をGArrowArray
(GObject
ベースのクラス)に対応させる。) -
GObject
のオブジェクトを作るときに対応するApache Arrowのオブジェクトの参照を増やす。 -
GObject
のオブジェクトを解放するときに対応するApache Arrowのオブジェクトの参照を減らす。
Apache ArrowのクラスとGObject
のクラスを1対1で対応させるのは簡単です。そうなるようにクラスを設計すればよいだけだからです。
GObject
のオブジェクトを作るときに対応するApache Arrowのオブジェクトの参照を増やすには、GObject
のオブジェクトのインスタンス変数としてstd::shard_ptr<ARROW_CLASS>
なインスタンス変数を用意して、GObject
のコンストラクターで代入します。(GArrowArray
ではプロパティーという仕組みを利用していますが、利用せずに直接構造体のメンバーを使ってもよいです。)
GObject
のオブジェクトを解放するときに対応するApache Arrowのオブジェクトの参照を減らすには、GObject
のオブジェクトが解放されるときに関数を呼ぶ仕組みがあるのでそれを利用します。(dispose()
とfinalize()
の2つ呼ばれる関数がありますが、この場合はfinalize()
を利用します。dispose()
は循環参照を解決するために利用します。)
arrow-glibでの実装
GArrowArray
を使って実際の実装を説明します。
まず、arrow::Array
(Apache Arrowのオブジェクト)の参照を増やすためのインスタンス変数を用意します。このインスタンス変数は外部から参照できる必要はないのでプライベートな構造体に持たせます。(カプセル化)
typedef struct GArrowArrayPrivate_ {
std::shared_ptr<arrow::Array> array;
} GArrowArrayPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(GArrowArray, garrow_array, G_TYPE_OBJECT)
#define GARROW_ARRAY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), GARROW_TYPE_ARRAY, GArrowArrayPrivate))
コンストラクターではstd::shard_ptr<arrow::Array>
を受け取ってこのインスタンス変数に設定します。(実際はプロパティーを使っているのでもう少し処理が増えています。)
GArrowArray *
garrow_array_new_raw(std::shared_ptr<arrow::Array> *arrow_array)
{
/* GARROW_ARRAY()はバリデーションつきでGArrowArray *にキャストするマクロ */
/* GARROW_TYPE_ARRAYはGArrowArray用のGTypeを返すマクロ */
/* GTypeはGObjectで型情報を表現するデータ */
auto array = GARROW_ARRAY(g_object_new(GARROW_TYPE_ARRAY, NULL));
/* GARROW_ARRAY_GET_PRIVATE()は前述のプライベートなデータ用の
構造体を返すマクロ */
auto priv = GARROW_ARRAY_GET_PRIVATE(array);
/* Apache Arrowのオブジェクトの参照を増やす */
priv->array = *arrow_array;
return array;
}
デストラクター(finalize()
)ではコンストラクターで増やした参照を減らします。
static void
garrow_array_finalize(GObject *object)
{
auto priv = GARROW_ARRAY_GET_PRIVATE(object);
/* priv->array.reset()でも同じ */
priv->array = nullptr;
G_OBJECT_CLASS(garrow_array_parent_class)->finalize(object);
}
まとめ
GObject Introspectionでの「一般的な」オブジェクトの寿命について説明しました。次回は本当に戻り値のオブジェクトの寿命について説明します。