闭包:一个函数加上它所访问的所有非局部变量(传入的参数?)
#include <math.h>
#include <glib-object.h>
void
g_cclosure_user_marshal_INT__VOID_VOID (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{
typedef gint (*GMarshalFunc_INT__VOID_VOID) (gpointer data1,
gpointer data2);
register GMarshalFunc_INT__VOID_VOID callback;
register GCClosure *cc = (GCClosure*) closure;
register gpointer data1, data2;
gint v_return;
g_return_if_fail (return_value != NULL);
g_return_if_fail (n_param_values == 1);
if (G_CCLOSURE_SWAP_DATA (closure))
{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}
else
{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GMarshalFunc_INT__VOID_VOID) (
marshal_data ? marshal_data : cc->callback);
v_return = callback (data1, data2);
g_value_set_int (return_value, v_return);
}
static void
compare (GClosure *closure, void *b)
{
GValue return_value = {0};
GValue param_value = {0};
g_value_init (&return_value, G_TYPE_INT);
g_value_init (¶m_value, G_TYPE_POINTER);
g_value_set_pointer (¶m_value, b);
g_closure_invoke (closure, &return_value, 1, ¶m_value, NULL);
gint r = g_value_get_int (&return_value);
if (r == -1) g_print ("a < b\n");
else if (r == 0) g_print ("a = b\n");
else g_print ("a > b\n");
g_value_unset (&return_value);
g_value_unset (¶m_value);
}
static gint
float_compare (void *a, void *b)
{
gfloat *f1 = a;
gfloat *f2 = b;
if (*f1 > *f2)
return 1;
else if (fabs (*f1 - *f2) <= 10E-6)
return 0;
else
return -1;
}
static gint
str_compare (void *a, void *b)
{
size_t len1 = g_utf8_strlen ((gchar *)a, -1);
size_t len2 = g_utf8_strlen ((gchar *)b, -1);
if (len1 > len2)
return 1;
else if (len1 == len2)
return 0;
else
return -1;
}
int
main (void)
{
g_type_init ();
gfloat a = 123.567;
gfloat b = 222.222;
GClosure *closure =
g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
compare (closure, &b);
g_closure_unref (closure);
gchar *s1 = "Hello World!\n";
gchar *s2 = "Hello!\n";
closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
compare (closure, s2);
g_closure_unref (closure);
return 0;
}
分析
g_cclosure_new函数创建了一个面向C语言的闭包。之所以在此强调是面向C语言的,那是因为GObject的闭包是面向任意语言的,例如pyg_closure_new函数可以创建python语言的闭包。返回值是一个GCClosure结构体。注意区别GCClosure和GClosure。
typedef struct _GClosure GClosure;
struct _GCClosure
{
GClosure closure; /*GObject提供的闭包结构*/
gpointer callback;/*指向回调函数的指针*/
};
/* GClosure结构体主要是面向各种语言对闭包的公有功能进行了基本抽象,这样所有要与GObject闭包机制打交道的语言,可以先继承GClosure结构体,然后根据自身需要再添加一些特点的数据成员。例如GCClosure就添加了callback无类型数据指针,用于指向闭包所对应的回调函数 */
struct _GClosure
{
/*< private >*/
volatile guint ref_count : 15;
/* meta_marshal is not used anymore but must be zero for historical reasons
as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */
volatile guint meta_marshal_nouse : 1;
volatile guint n_guards : 1;
volatile guint n_fnotifiers : 2; /* finalization notifiers */
volatile guint n_inotifiers : 8; /* invalidation notifiers */
volatile guint in_inotify : 1;
volatile guint floating : 1;
/*< protected >*/
volatile guint derivative_flag : 1;
/*< public >*/
volatile guint in_marshal : 1;
volatile guint is_invalid : 1;
/*< private >*/ void (*marshal) (GClosure *closure,
GValue /*out*/ *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
/*< protected >*/ gpointer data;
/*< private >*/ GClosureNotifyData *notifiers;
};
GClosure *closure = g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
以上两行代码实现了下图所示的数据结构。GClosure主要了解三个成员是marshal、data与marshal_data。其中marshal是一个函数指针,指向一个回调函数的调用者;data与marshal_data皆为gpointer指针类型。对于C语言闭包,data是向回调函数传输数据的地址,而marshal_data则指向回调函数。
callback指针和marshal_data指针都指向回调函数。因为GObject库允许你自由切换回调函数,可将marshal_data看作是一个开关,它可以暂时屏蔽GCClosure中的callback所指向的回调函数,而启用marshal_data所指向的回调函数。
compare函数运行流程如图
我的理解g_cclosure_user_marshal_INT__VOID_VOID 函数就是判断运行callback指向的回调还是marshal_data指向回调函数。最终的浮点a、b和float_compare都会在g_cclosure_user_marshal_INT__VOID_VOID函数中出现。
*如何生成g_cclosure_user_marshal_函数?
GLib 库提供了一个名为 glib-genmarshal 的工具,它可以根据我们给出的函数描述信息产生有效的 marshal 代码。上文中的 g_cclosure_user_marshal_INT__VOID_VOID 函数,我便是使用这个工具产生的。
首先,准备好一份文本文档,例如 in__void_void.txt:
INT:VOID,VOID
然后,执行命令:
$ glib-genmarshal --body int__void_void.txt > int__void_void.c
即可产生 g_cclosure_user_marshal_INT__VOID_VOID 函数。