SSブログ

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でも使うというのがもっとも正しい方向だけど)....

4  deep dive into aravis

aravisのオブジェクト指向風の動作はGObjectというCで書かれたライブラリを使うことで実現している。

aravisの実装を辿ろうとするとどうしてもGObjectを知らないといけない、ということになる。

4.1  aravisのオブジェクト指向

GObjectは
  • 単一継承のオブジェクトシステム
  • GTypeという整数定数で型が区別される
  • 基底クラスGObjectを持つ
  • クラスのインスタンスは構造体であり、先頭にオブジェクトを識別するポインタがある
  • それとは別に仮想関数を実装するための構造体がある
  • reference countingによるメモリ管理
  • signal/closureという非同期のメッセージングシステムがある
というような特徴を持っていて、macOSのCoreFoundationとObjective-Cのランタイムによく似ている。

口の悪い誰かが「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;
のような構造体だとなっている。クラス構造体は
  1. GTypeClass構造体
  2. 関数ポインタ
からなっていて、インスタンス構造体は
  1. GTypeInstance構造体
  2. インスタンス変数
からなっているらしい。

それぞれの先頭にあるメンバは
struct _GTypeClass
{
    GType g_type;
};
struct _GTypeInstance
{
    GTypeClass *g_class;
};
のような構造体が埋まることになる。

つまり
0618structs.png
のようになっているらしい。ようするにオブジェクト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;
となって、それぞれ先頭に親クラスの構造体をそのまま内包する。

つまり
0618derivedstructs.png
のようなことになっている。このときオブジェクトBに内包されたオブジェクトAのGTypeClassポインタはオブジェクトAではなく、オブジェクトBのGTypeの値を指すようになっている。

BからさらにCを派生させる場合も同じでBを内包することでやはり先頭はGTypeClassポインタがあって、それはCのGTypeを指すようになっている。

つまりちょうどObjective-Cのisaポインタのような役割のポインタがいつも構造体の先頭にある、ということになる。ややこしいけど、まあそうなるわな、という感じ。

クラス構造体のメンバにある関数ポインタの役割は次回。
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。