对g_signal_new()参数的解释
在GObject中,如果想给自己写的类加上signal,一般需要在*_class_init函数中使用g_signal_new函数,但是这个函数的参数比较复杂:
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, ...);
从该函数的原型中,我们可以看出,该函数的参数个数是可变的。
下面,我姑妄从前往后依次解释一下各个参数的含义:
- const gchar *signal_name:该参数是信号的名字,由分隔符以及ASCII码中的字母和数字构成,并且第一个字符必须是字母。分隔符可以是"-"或"_"——事实上,系统会先调用g_strdelimit把"_"转化为"-"再存储signal_name。因此,在调用g_singal_emit_by_name时,detailed_signal参数中的分隔符必须是"-"。
- GType itype:该参数是signal所依附的类的在GType类型系统中注册时得到的ID,也就是*_get_type()函数的返回值。
-
GSignalFlags signal_flags:该参数是信号的属性标记,有七种,我将在最后解释:
- G_SIGNAL_RUN_FIRST
- G_SIGNAL_RUN_LAST
- G_SIGNAL_RUN_CLEANUP
- G_SIGNAL_NO_RECURSE
- G_SIGNAL_DETAILED
- G_SIGNAL_ACTION
- G_SIGNAL_NO_HOOKS
- guint class_offset:该参数是itype对应的类的class结构中的一个函数指针相对于class结构的实例的首地址的偏移量。该函数指针所对应的函数常被称为"per-object handler","default (signal) handler"或"object methond handler",并将在信号发出后被调用(如调用g_signal_emit_by_name)。常配合宏G_STRUCT_OFFSET使用(该宏能够返回结构体变量的成员相对于该结构体的变量的首地址的偏移量)。如果设为0,则表示该类没有"per-object handler"。
-
GSignalAccumulator accumulator:该参数是一个函数指针,其对应的函数将在该信号的每个handler执行完以后执行。GSignalAccumulator类型的定义如下:
typedef gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data);
从中可以看出该函数指针类型有4个参数: -
GSignalInvocationHint *ihint:该参数是一个GSignalInvocationHint类型的指针,而GSignalInvocation结构的定义如下:
typedef struct { guint signal_id; GQuark detail; GSignalFlags run_type; } GSignalInvocationHint;
其中,signal_id是导致GSignalAccumulator所指函数被调用的signal的ID(因为该信号signal调用了某个handler)。detail是该signal的detail部分对应的GQuark(其实就是字符串的散列值)。run_type则反映了handler被调用时signal发射进行到的阶段,其值为 G_SIGNAL_RUN_FIRST,G_SIGNAL_RUN_LAST或G_SIGNAL_CLEANUP。
- GValue *return_accu:该参数是一个GValue类型的指针,可以被程序员用来返回任何在GType类型系统注册的,带有value_table的类型。
- const GValue *handler_return:该参数是一个GValue类型的指针,指向一个GValue变量,该变量存储了GSignalAccumulator所指函数被调用前,信号调用的handler的返回值,可以用g_value_get_*系列函数获取其存储的值。
- gpointer data:该参数即为g_signal_new的参数gpointer accu_data的值
显然,该函数的返回类型为gboolean。如果其返回值为FASLE,则signal发射过程就会被中止(即不再调用后面的hander),否则会继续下去。事实上,"delete-event"等带有event后缀的signal就是利用了这一点——这些信号的某个回调函数如果返回了TRUE,则以后的回调函数就不会被调用。
我们可以看一下"delete-event"的accumulator, _gtk_boolean_handled_accumulator的代码:
gboolean _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy) { gboolean continue_emission; gboolean signal_handled; signal_handled = g_value_get_boolean (handler_return); g_value_set_boolean (return_accu, signal_handled); continue_emission = !signal_handled; return continue_emission; }
注意,如果该signal有accumulator,该signal的回调函数类型(由c_marshaller反映)必须有返回值,否则该signal不能有accumulator,也即在调用g_signal_new时以NULL作为该形参的实参。
- gpointer accu_data:该参数将作为用户自定义参数传入accumulator所指向的函数中。
- GSignalCMarshaller c_marshaller:该参数是一个GSignalCMarshall类型的函数指针,其值反映了回调函数的返回值类型和额外参数类型(所谓“额外参数”,即指除回调函数中instance和user_data以外的参数)。
例如,g_closure_marshal_VOID_VOID说明该signal的回调函数为以下的callback类型:
typedef void (*callback) (gpointer instance, gpointer user_data);
而g_closure_marshal_VOID_POINTER则说明该signal的回调函数为以下的callback类型:
typedef void (*callback) (gpointer instance, gpointer arg1, gpointer user_data);
如果默认提供的GClosureMarshall中没有你需要的,你可以用glib-genmarshall生成它,具体可参见devhelp中有关glib-genmarshall的说明。
- GType return_type:该参数的值应为回调函数的返回值在GType类型系统中的ID。
- guint n_params:该参数的值应为回调函数的额外参数的个数。
- ...:这一系列的参数的值应为回调函数的额外参数在GType类型系统中的ID,且这一系列参数中第一个参数的值为回调函数的第一个额外参数在GType类型系统中的ID,依次类推。
最后,我们来解释一下GSignalFlags中各个特征标志的含义:
- G_SIGNAL_RUN_FIRST:调用回调函数时,"per-object handler"对应的回调函数将第一个调用
- G_SIGNAL_RUN_LAST:调用回调函数时,"per-object handler"对应的回调函数将在用户用g_signal_connect连接的回调函数之后调用,并在用户用g_signal_connect_after连接的回调函数之前调用
- G_SIGNAL_RUN_CLEANUP:调用回调函数时,"per-object handler"对应的回调函数将最后一个调用
- G_SIGNAL_NO_RECURSE:信号发射时,如果信号的上次发射还没有结束,那么本次信号发射将不再进行,而只是使上次的信号发射重新开始。
- G_SIGNAL_DETAILED:信号名字可以使用"signal_name::detailed"的形式。
- G_SIGNAL_ACTION:程序员可以在代码中自由地调用g_signal_emit族的函数来发射信号,而不需要把g_signal_emit族的函数放在一段代码中再来调用。
- G_SIGNAL_NO_HOOKS:信号发射过程中不支持钩子函数。
例子请看cloverprice的文章:《GObject 08: A class with a signal》
传送门:http://cloverprince.javaeye.com/blog/500964
注:信号的发射以我的理解,就是按照要求调用回调函数的过程……
2011年1月28日 08:56
GObject里面最恶心的就是和SignalNode以及Closure相关的东西了....
不管怎样,支持一下....
2011年1月28日 09:04
@pingf: 还好吧,用起来很舒服......
2011年10月12日 20:12
很讨厌闭包,文章写的很好
2012年6月11日 19:16
最近在学GObject,感觉很吃力啊