ソースコードは http://d.hatena.ne.jp/propella/20080304/p1 と同じ物を使います。前回よく分からなくて飛ばした話から。
Gtk のオブジェクトシステムである GObject は、それだけでかなり面白い機構です。やたら複雑な理由は、静的型、動的型を問わず他の言語との協調を主眼に置いているからで、自分の事だけ考えていればよい C++ や Java とはそもそも志が違うのだという事です。しかしここでは後ろ髪を引かれながらも最低限の事だけ調べます。
gtk_container_add (GTK_CONTAINER(window), scrolledWindow);
gtk_window_new 等のコンストラクタは GtkWidget オブジェクトを返すので、GtkWindow 特有の関数を使う場合は GTK_CONTAINER 等のキャストマクロを使います。もちろん継承関係にあるタイプにだけキャスト出来るので、GTK_TEXT_VIEW(window) のような事をすると警告が出ます。
前回 Gtk はキャストが多すぎてデバッグがしにくいと書きましたが、GOject の強力なリフレクション機能が役に立ちます。例えばデバッグの途中でwindow のタイプが分からなくなったら G_OBJECT_TYPE_NAME を使い、次ようにして思い出す事が出来ます。
(gdb) p (char*) G_OBJECT_TYPE_NAME(window) $14 = 0x4d5e80 "GtkWindow"
Glib ではクラスの無いタイプも作れるようなので、タイプとクラスを区別して書きます。クラスと言うと、Smalltalk のいわゆるクラスオブジェクトの事を言うみたいです。GObject のタイプを定義するには、オブジェクト定義とクラス定義にそれぞれ構造体を用意します。ざっくり言ってここも Smalltalk と同じで、インスタンスごとのメンバはオブジェクト定義に、メソッドなどの宣言はクラス定義に書きます。
色々ある決まりの一つに、最初の要素は親タイプだというのがあります。別の言い方をすると、サブタイプとは、第一要素である親タイプの機能に、新たに第二要素以下の機能を追加した物と言えます。という事は、親タイプのメンバにアクセスするためには、第一要素を使うかキャストしないといけません。
(gdb) p (char *) G_OBJECT_TYPE_NAME(GTK_WINDOW(window)->bin->child) $39 = 0x4ae160 "GtkScrolledWindow" (gdb) p (char *) G_OBJECT_TYPE_NAME(GTK_BIN(window)->child) $40 = 0x4ae160 "GtkScrolledWindow"
ポリモルフィズムの仕組みは相当分かりにくいので、じっくり迫ってみます。例えば gtk_container_add() の定義ではただ add シグナルを発信するという事になっていて、具体的な事は書かれていません。まずここで、シグナルを定義する事でインスタンスごとに gtk_container_add() の動作を変更する事が出来ます。
次に、ADD シグナルの定義はこんな感じ。
container_signals[ADD] = g_signal_new (I_("add"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkContainerClass, add), NULL, NULL, _gtk_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WIDGET);
G_STRUCT_OFFSET (GtkContainerClass, add) マクロでこのシグナルと GtkContainerClass の add メンバを関連付けます。デフォルトで何もしない関数ポインタが割り当てられています。サブタイプでオーバーロードしたいときは初期化時に違う関数を割り当てたらいいのです。例えば、GtkTextView ではgtk_text_view_class_init (GtkTextViewClass *klass) の中で、add の定義は次のようになっています。
container_class->add = gtk_text_view_add;
ちなみに GtkWindow の第一要素が GtkBin で GtkBin の第一要素が GtkContainer となっているので、M-x gud-watch で継承関係が見れて便利です。
Watch Expressions: 0:[-] GTK_WINDOW(window) struct _GtkWindow * 0x8f3e800 1: [-] bin GtkBin 2: [-] container GtkContainer 3: [+] widget GtkWidget 3: [+] focus_child GtkWidget * 0x0 3: [?] border_width 0 3: [?] need_resize 1 3: [?] resize_mode 1 3: [?] reallocate_redraws 0 3: [?] has_focus_chain 0 2: [+] child GtkWidget * 0x0 1: [?] title 0x0 1: [?] wmclass_name 0x8f44d18 "text" ...
本当は GtkTextView の構造を調べるつもりだったけど、ここで力尽きました。。。