aravis解析 その4 [aravis]
ということで、これまで(その1、その2、その3)でほんとうにざっとaravisの使い方をおさらいしてみた。みてわかるように、aravisを使うのは非常に簡単になっている。それはGObjectが使いやすくできている(書きやすいとは言ってない)のと、aravisがGObjectの思想に忠実に従ってできているからだろう。
最初aravisのswiftラッパを作ろうと思っていたんだけど、ほとんどそんな必要はないぐらい簡単だし、そんなものを作ってもaravisのクラスをswiftのクラスに読み替えて、文字列の変換をして、パブリックなメソッドが単にaravisの関数を呼ぶだけのものになってしまう。
Objective-Cラッパなら少しは意味があったかもしれないけど、今更そんなものを作るつもりはない。
そうするとやはりGObjectをswiftのクラス/構造体で書き換えて、glibやgettextやintltoolなどのlinux固有のライブラリの依存をなくすというのが正しい方向だろう(本当は、そんなものをやめて素直にaravisをそのままmacOSでも使うというのがもっとも正しい方向だけど)....
aravisの実装を辿ろうとするとどうしてもGObjectを知らないといけない、ということになる。
口の悪い誰かが「poor man's Objective-C」と呼んだりしている。
GObjectは使うのは簡単だけど、自分で新しいクラスを定義しようとすると、CoreFoundationと同じように、大量のボイラープレートコードを書かないといけない。CoreFoundationではプログラマが派生クラスを書くことを諦めてしまって、Appleが用意したオブジェクトを使うだけになっているけど、GObjectはそれ単独では役に立たないのでクラスを派生させる(サブクラスを作る)手段を解放している。
おそらくObjective-CのランタイムとCoreFoundationとに似たことをやっているんだろうと思う。しかたないのでGObjectのリファレンスマニュアルをざっくりみることにする。
のような構造体だとなっている。クラス構造体は
それぞれの先頭にあるメンバは
のような構造体が埋まることになる。
つまり のようになっているらしい。ようするにオブジェクトAの先頭はGTypeの値を指すポインタになっている。
オブジェクトAのサブクラスのオブジェクトBのそれぞれの構造体は
となって、それぞれ先頭に親クラスの構造体をそのまま内包する。
つまり のようなことになっている。このときオブジェクトBに内包されたオブジェクトAのGTypeClassポインタはオブジェクトAではなく、オブジェクトBのGTypeの値を指すようになっている。
BからさらにCを派生させる場合も同じでBを内包することでやはり先頭はGTypeClassポインタがあって、それはCのGTypeを指すようになっている。
つまりちょうどObjective-Cのisaポインタのような役割のポインタがいつも構造体の先頭にある、ということになる。ややこしいけど、まあそうなるわな、という感じ。
クラス構造体のメンバにある関数ポインタの役割は次回。
最初aravisのswiftラッパを作ろうと思っていたんだけど、ほとんどそんな必要はないぐらい簡単だし、そんなものを作ってもaravisのクラスをswiftのクラスに読み替えて、文字列の変換をして、パブリックなメソッドが単にaravisの関数を呼ぶだけのものになってしまう。
Objective-Cラッパなら少しは意味があったかもしれないけど、今更そんなものを作るつもりはない。
そうするとやはりGObjectをswiftのクラス/構造体で書き換えて、glibやgettextやintltoolなどのlinux固有のライブラリの依存をなくすというのが正しい方向だろう(本当は、そんなものをやめて素直にaravisをそのままmacOSでも使うというのがもっとも正しい方向だけど)....
4 deep dive into aravis
aravisのオブジェクト指向風の動作はGObjectというCで書かれたライブラリを使うことで実現している。aravisの実装を辿ろうとするとどうしてもGObjectを知らないといけない、ということになる。
4.1 aravisのオブジェクト指向
GObjectは- 単一継承のオブジェクトシステム
- GTypeという整数定数で型が区別される
- 基底クラスGObjectを持つ
- クラスのインスタンスは構造体であり、先頭にオブジェクトを識別するポインタがある
- それとは別に仮想関数を実装するための構造体がある
- reference countingによるメモリ管理
- signal/closureという非同期のメッセージングシステムがある
口の悪い誰かが「poor man's Objective-C」と呼んだりしている。
GObjectは使うのは簡単だけど、自分で新しいクラスを定義しようとすると、CoreFoundationと同じように、大量のボイラープレートコードを書かないといけない。CoreFoundationではプログラマが派生クラスを書くことを諦めてしまって、Appleが用意したオブジェクトを使うだけになっているけど、GObjectはそれ単独では役に立たないのでクラスを派生させる(サブクラスを作る)手段を解放している。
4.2 GObject
GObjectがどういうふうになってるか、aravisを読める程度に調べてみる。やりだすとこれはおそらく大変なので適当なところで引き上げることにする。おそらくObjective-CのランタイムとCoreFoundationとに似たことをやっているんだろうと思う。しかたないのでGObjectのリファレンスマニュアルをざっくりみることにする。
4.2.1 GType
型ごとに一意に割り当てられた定数。オブジェクトではないスカラ型にも割り当てられているらしい。新しくクラスを定義するには、登録という作業をしてその中でダイナミックに定数が割り当てられるようである。登録の作業そのものは、最初にその型のインスタンスが作られたときとかに実行されるようである。4.2.2 オブジェクト定義
オブジェクトとして機能するために2つの構造体を定義する必要がある。- クラス構造体
- インスタンス構造体
/* A definitions */ typedef struct { GTypeClass parent_class; void (*method_a) (void); void (*method_b) (void); } AClass; typedef struct { GTypeInstance parent; int field_a; int field_b; } A;
- GTypeClass構造体
- 関数ポインタ
- GTypeInstance構造体
- インスタンス変数
それぞれの先頭にあるメンバは
struct _GTypeClass { GType g_type; }; struct _GTypeInstance { GTypeClass *g_class; };
つまり のようになっているらしい。ようするにオブジェクトAの先頭はGTypeの値を指すポインタになっている。
オブジェクトAのサブクラスのオブジェクトBのそれぞれの構造体は
typedef struct { A parent; int field_c; int field_d; } B; typedef struct { AClass parent_class; void (*method_c) (void); void (*method_d) (void); } BClass;
つまり のようなことになっている。このときオブジェクトBに内包されたオブジェクトAのGTypeClassポインタはオブジェクトAではなく、オブジェクトBのGTypeの値を指すようになっている。
BからさらにCを派生させる場合も同じでBを内包することでやはり先頭はGTypeClassポインタがあって、それはCのGTypeを指すようになっている。
つまりちょうどObjective-Cのisaポインタのような役割のポインタがいつも構造体の先頭にある、ということになる。ややこしいけど、まあそうなるわな、という感じ。
クラス構造体のメンバにある関数ポインタの役割は次回。
2019-06-23 22:32
nice!(0)
コメント(0)
コメント 0