Wednesday, January 2, 2008

gobject 的 marshaller

在剛開始使用 GObject 很容易遇到的一個問題是,marshaller 是什麼?因為在幫自己的物件設計 signal 的時候,有一項資訊就是 marshaller:
guint
g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
guint class_offset,
GSignalAccumulator accumulator,
gpointer accu_data,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...);

雖然說搜尋 glib-genmarshal 可以找到很多生成 c_marshaller 的方法,但我們更感興趣的是為什麼會需要 marshaller 的存在。

這邊的 marshaller 更精準的說法是 closure marshaller。在 gobject 中,我們利用 g_signal_connect 連接 signal handler 時,所提供的 C 函數會先被包裝成一個 closure,然後這個 closure 才真正地連接上去。上面提到的 c_marshaller 則被用來作為這個 closure 的 closure marshaller。

Marshaller 自己本身也是 C 函數。在 signal 發生時,gobject 並不會直接呼叫 signal handler,而是呼叫該 handler 對應的 closure marshaller。換句話說,呼叫的動作變成一種間接的行為。

這層間接提供的好處是,我們的 signal handler 不一定要用 C 來寫。只要提供適當的 closure marshaller,signal handler 可以用任何語言來寫。當然這時就得用比 g_signal_connect 提供更大彈性的
gulong
g_signal_connect_closure (gpointer instance,
const gchar *detailed_signal,
GClosure *closure,
gboolean after);

來連接 signal handler。

gobject 的各種 bindings 就可以充份利用 gobject 的這項機制。像是 Perl binding 即提供了 gperl_closure_marshal 來當 marshaller,溝通在 Perl 與 C 之間。

Closure 是 gobject 用來一般化 callback function 的方法。伴隨著 callback function,資料也會在不同的語言中交換。對於資料,gobject 是用 GValue 來處理交換的問題。雖然用了不同的名詞,但G_VALUE_LCOPY 跟 G_VALUE_COLLECT 實際上就是 GValue 給 C 通用的 marshaller 跟 demarshaller。

要更清楚了解 marshaller/demarshaller 的意義,除了各 bindings 外,也可以參考 dbus-glib 的實作。在裡頭,可以看到 dbus message arguments 怎麼被 demarshal 成 GValue's 再被 marshal 成 C 型別;或者是相反過來的程序。dbus-glib 為了效能考量,有做了一些壞事。這些壞事反而是了解 gobject 很好的參考。