Gobject的闭包
void qsort (void*base, size_t nmemb, size_t size, int(*compar) (constvoid*, constvoid*));
闭包(Closure)的概念
我们通过函数指针向 qsort 函数传入了一个函数 str_compare,这个函数被称为回调函数,但是它还有一个比较深奥的名字——“闭包”。
所谓闭包,简而言之,就是一个函数加上它所访问的所有非局部变量,而所谓“非局部变量”,表示这些变量对于那个函数而言既非局部变量,也非全局变量。
我们向 qsort 传入函数 str_compare,它所接受排序数据集合中的 2 个元素,而且 2 个元素对于 str_compare 而言,既非是全局变量,也非其局部变量,因此 str_compare 与这 2 个参数形成了一个闭包。
在许多动态语言中,闭包通常也被昵称为“函数是第一类对象”,即函数与那些语言中基本类型具有相同的权利,例如函数可以存储在变量中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。
在 C 语言中,利用函数指针并配合参数的复制与传递,可模拟闭包这种结构,但是在可读性上没有那些内建支持闭包的语言优雅。
GObject 提供了 GClosure 对象与方法,实现了功能比较全面的 C 闭包模拟,我们可以在程序中直接使用它。下面,通过一个很小的示例,演示 GClosure 的使用。
先来看一个非 GClosure 的 C 闭包示例:
#include <stdio.h>
#include <math.h>
#include <string.h>
typedef_GCClosure GCClosure;
struct_GCClosure {
GClosure closure;
gpointer callback;
};
typedefint(*Func) (void*, void*);
staticvoid
compare (void*a, void*b, Func callback)
{
int r = callback (a, b);
if(r == -1)
printf("a < b\n");
else if(r == 0)
printf("a = b\n");
else
printf("a > b\n");
}
static int
float_compare (void*a, void*b)
{
float*f1 = (float*)a;
float*f2 = (float*)b;
if(*f1 > *f2)
return1;
elseif(fabs(*f1 - *f2) <= 10E-6)
return0;
else
return-1;
}
staticint
str_compare (void*a, void*b)
{
size_tlen1 = strlen((char*)a);
size_tlen2 = strlen((char*)b);
if(len1 > len2)
return1;
elseif(len1 == len2)
return0;
else
return-1;
}
int
main (void)
{
floata = 123.567;
floatb = 222.222;
Func func = float_compare;
compare (&a, &b, func);
char*s1 = "hello world!";
char*s2 = "hello!";
func = str_compare;
compare (s1, s2, func);
return0;
}
上述代码主要实现了一个 compare 函数,它可以比较两个任意类型数据的大小,前提是你要向它提供特定的回调函数(闭包),例如代码中的 float_compare 与 str_compare 函数,它们分别实现了浮点数比较与字符串比较。
将上述程序改为 GClosure 实现,如下:
#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,
constGValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{
typedefgint (*GMarshalFunc_INT__VOID_VOID) (gpointer data1,
gpointer data2);
registerGMarshalFunc_INT__VOID_VOID callback;
registerGCClosure *cc = (GCClosure*) closure;
registergpointer 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);
}
staticvoid
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");
elseif(r == 0)
g_print ("a = b\n");
else
g_print ("a > b\n");
g_value_unset (&return_value);
g_value_unset (¶m_value);
}
staticgint
float_compare (void*a, void*b)
{
gfloat *f1 = a;
gfloat *f2 = b;
if(*f1 > *f2)
return1;
elseif(fabs(*f1 - *f2) <= 10E-6)
return0;
else
return-1;
}
staticgint
str_compare (void*a, void*b)
{
size_tlen1 = g_utf8_strlen ((gchar *)a, -1);
size_tlen2 = g_utf8_strlen ((gchar *)b, -1);
if(len1 > len2)
return1;
elseif(len1 == len2)
return0;
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);
return0;
}
下面这张图可以用来解释。
GLib 库提供了一个名为 glib-genmarshal 的工具,它可以根据我们给出的函数描述信息产生有效的 marshal 代码。上文中的 g_cclosure_user_marshal_INT__VOID_VOID 函数,我便是使用这个工具产生的。