一、回调函数
首先得知道回调函数的知识,C语言回调函数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
str_compare (const void *s1, const void *s2)
{
char *str1 = *(char **)s1;
char *str2 = *(char **)s2;
size_t l1 = strlen (str1);
size_t l2 = strlen (str2);
if (l1 > l2)
return 1;
else if (l1 == l2)
return 0;
else
return -1;
}
int
main (void)
{
char *str_array[5] = {"a", "abcd", "abc", "ab", "abcde"};
qsort (str_array, 5, sizeof (char *), str_compare);
for (int i = 0; i< 5; i++)
printf ("%s ", str_array[i]);
printf ("\n");
return 0;
}
二、闭包
在上述中的str_compare函数被称为回调函数,这个回调函数加上他的两个参数就形成了一个闭包。
三、GClosure:GClosure是GObject 提供的对象与方法,实现了功能比较全面的 C 闭包模拟,我们可以在程序中直接使用它。
1、GClosure 包含两个结构体
首先是GClosure结构体
struct GClosure{
gpointer data指针;
marshal函数指针;
gpointer marshal_data指针;
};
然后是GCClosure结构体
typedef _GCClosure GCClosure;
struct _GCClosure {
GClosure closure; //继承了GClosure结构体
gpointer callback; //gpointer是void*的别名,所以callback就是一个无类型指针
};
它们之间的数据结构体如下:图片来自http://garfileo.is-programmer.com/2011/3/20/function-pointer-and-callback-function-and-closure.25453.html
以下就是新建了一个闭包
GClosure *closure = g_cclosure_new (G_CALLBACK (float_compare), &a, NULL);
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
所以就很清楚:
callback指针指向了回调函数。
data指针指向的是要传给回调函数的第一个参数。
marshal指针指向了g_cclosure_user_marshal_*函数,这个又臭又长的恶心函数应该是通用的,可以用命令产生里面的代码。
marshal_data指针是指向回调函数的,但是已经有callback指针指向回调函数了呀,其实marshal_data可以看作是一个开关,它可以暂时屏蔽 GCClosure 中的 callback 所指向的回调函数,而启用 marshal_data 所指向的回调函数。。
可以通过 g_closure_set_meta_marshal 函数设置了 closure 闭包的 marshal_data 指针,此时指向了marshal_data 指针 str_compare_new 函数,而callback的值会是marshal_data 。
g_closure_set_meta_marshal (closure, str_compare_new,
g_cclosure_user_marshal_INT__VOID_VOID);
闭包调用的这个过程如下:
g_closure_invoke函数是库里面有的,不用自己定义。
上图也就是:把闭包和回调函数的其他参数传入到compare函数里,在compare函数里调用g_closure_invoke函数,g_closure_invoke函数需要的参数有闭包和包含回调函数其他参数的容器,g_closure_invoke会调用那个恶心的函数,那个恶心的函数调用回到函数,具体传入什么参数,返回什么值上图都很清楚。
完整代码,
#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
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;
}
static gint
str_compare_new (void *a, void *b)
{
g_print ("\nI'm a new marshaller\n");
return (str_compare (a, b));
}
int
main (void)
{
g_type_init ();
gchar *s1 = "Hello World!\n";
gchar *s2 = "Hello!\n";
GClosure *closure = g_cclosure_new (G_CALLBACK (str_compare), s1, NULL);
//第一次调用的compare的时候,回调函数是str_compare,callback指向它
g_closure_set_marshal (closure, g_cclosure_user_marshal_INT__VOID_VOID);
compare (closure, s2);
//第二次调用的时候,以下函数更改了marshal_data指向的回调函数为str_compare_new
g_closure_set_meta_marshal (closure, str_compare_new,
g_cclosure_user_marshal_INT__VOID_VOID);
compare (closure, s2);
g_closure_unref (closure);
return 0;
}