官网API:https://developer.gnome.org/gobject/stable/
本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:
楼主见解:
主要讲解信号g_signal_new的第5/6个参数。暂时没想到用武之地。
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,
...);
gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer data);
其中,第 5 个参数是 accumulator,其类型为 GSignalAccumulator,这是一个函数指针类型,g_signal_new 的第 6 个参数 accu_data 会被 g_signal_new 传递于 accumulator 所指向的函数,作为后者的第 4 个参数 data。
accumulator 所指向的函数会在信号所连接的每个闭包(包括在信号注册阶段生成的信号默认闭包,以及信号连接阶段中信号使用者所提供的闭包)执行之后被调用,它的主要功能就是收集信号所连接的各个闭包的返回值(简称“信号返回值”)。
此闭包返回值,由 glib-genmarshal 工具自动生成。
源博客网址:http://garfileo.is-programmer.com/categories/6934/posts
GObject 信号机制——信号 Accumulator
在文档 [1] 中,从外围对 GObject 信号注册的过程进行了初步分析。生命不息,折腾不止,我们应当以 Adrian Hands 大叔为榜样。所以,本文继续解决文档 [1] 中遗留的问题,即 g_signal_new 函数的第 5 个与第 6 个参数的含义。
首先,再次回放一下 g_signal_new 函数的众参数:
1 2 3 4 5 6 7 8 9 10 |
|
其中,第 5 个参数是 accumulator,其类型为 GSignalAccumulator,这是一个函数指针类型,即:
1 2 3 4 |
|
g_signal_new 的第 6 个参数 accu_data 会被 g_signal_new 传递于 accumulator 所指向的函数,作为后者的第 4 个参数 data。
至此,从字面上对 g_signal_new 函数的第 5 个与第 6 个参数的含义便分析完毕了。当然,这远远不够。因为现在还不是很清楚 accumulator 这个函数指针的作用。
简单的说,accumulator 所指向的函数会在信号所连接的每个闭包(包括在信号注册阶段生成的信号默认闭包,以及信号连接阶段中信号使用者所提供的闭包)执行之后被调用,它的主要功能就是收集信号所连接的各个闭包的返回值(简称“信号返回值”)。
至于 accumulator 这个单词的汉译,在文档 [1] 的评论中 pingf 有一些建议。在此,我就不翻译了,而直呼其名。
为了更直观的理解信号 accumulator 的作用,可以对文档 [1] 中的实例进行一些修改,构建一个新的实例。
首先,照抄 SignalDemo 类的头文件 signal-demo.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
然后对文档 [1] 中的 SignalDemo 类的源文件 signal-demo.c 进行一些修改,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
|
相对于文档 [1] 中的 signal-demo.c,上述代码主要增加了两个宏和一个函数:
1 2 3 4 |
|
这些代码不需要手工编写,我们可以使用文档 [2] 中提及的 glib-genmarshal 工具生成它们。
g_cclosure_user_marshal_INT__STRING 函数(其返回值为 gint 类型)是作为信号默认闭包的 marshal 使用的,用于替换文档 [1] 中的 g_cclosure_marshal_VOID__STRING 函数(无返回值),即:
1 2 3 4 5 6 7 8 9 10 11 |
|
之所以替换文档 [1] 中的 g_cclosure_marshal_VOID__STRING 函数,是因为信号的 accumulator 需要信号存在返回值。
另外,上述代码中将文档 [1] 中 g_signal_new 函数的第 3 个参数由“G_SIGNAL_RUN_FIRST” 修改为“G_SIGNAL_RUN_LAST”,表示信号默认闭包设置为信号使用者的闭包调用结束后方被调用,也可以修改为“G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST”,这表示信号默认闭包会在信号使用者的闭包调用之前与之后被调用。至于为什么需要如此修改,还是暂且作为一个悬念留给番外篇 (3) 去解释吧,我们尽量遵守 KISS 原则,一篇文章只解决一个问题。
下面开始修改文档 [1] 中的 SignalDemo 类的调用者,即 main.c 文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
相对于文档 [1] 中的 main.c,上面代码所做的变动只有两处:
- my_signal_handler 函数被重新定义为带有 gint 类型返回值的函数。
- g_signal_emit_by_name 函数的调用多出一个无用的参数 val。之所以说这个参数没有用,是因为在本文的示例中,我们使用了信号 accumulator。如果我们没有使用信号 accumulator 的话,这个 val 参数可以保存最后一个信号闭包的返回值。虽然它在本文中没有用,但是它的存在是有意义的,可保证本文的示例不会出现段错误。
编译这个新的示例并执行编译结果:
1 2 3 4 5 6 7 8 9 10 |
|
根据 test 程序的输出结果去印证上述的程序源码,信号 accumulator 的工作背景及其过程便昭然在目。
参考文档
转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com