GObject学习教程---第十三章:GObject 信号机制——信号 Accumulator

索引:https://blog.csdn.net/knowledgebao/article/details/84633798

本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:

楼主见解:

主要讲解信号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

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,

                    ...);

其中,第 5 个参数是 accumulator,其类型为 GSignalAccumulator,这是一个函数指针类型,即:

1

2

3

4

gboolean (*GSignalAccumulator) (GSignalInvocationHint *ihint,

                                GValue *return_accu,

                                const GValue *handler_return,

                                gpointer data);

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

#ifndef SIGNAL_DEMO_H

#define SIGNAL_DEMO_H

   

#include <glib-object.h>

   

#define SIGNAL_TYPE_DEMO (signal_demo_get_type ())

#define SIGNAL_DEMO(object) \

        G_TYPE_CHECK_INSTANCE_CAST ((object), SIGNAL_TYPE_DEMO, SignalDemo)

#define SIGNAL_IS_DEMO(object) \

        G_TYPE_CHECK_INSTANCE_TYPE ((object), SIGNAL_TYPE_DEMO))

#define SIGNAL_DEMO_CLASS(klass) \

        (G_TYPE_CHECK_CLASS_CAST ((klass), SIGNAL_TYPE_DEMO, SignalDemoClass))

#define SIGNAL_IS_DEMO_CLASS(klass) \

        (G_TYPE_CHECK_CLASS_TYPE ((klass), SIGNAL_TYPE_DEMO))

#define SIGNAL_DEMO_GET_CLASS(object) (\

                G_TYPE_INSTANCE_GET_CLASS ((object), SIGNAL_TYPE_DEMO, SignalDemoClass))

   

typedef struct _SignalDemo SignalDemo;

struct _SignalDemo {

        GObject parent;

};

   

typedef struct _SignalDemoClass SignalDemoClass;

struct _SignalDemoClass {

        GObjectClass parent_class;

        void (*default_handler) (gpointer instance, const gchar *buffer, gpointer userdata);

};

   

GType signal_demo_get_type (void);

 

#endif

然后对文档 [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

#include "signal-demo.h"

 

G_DEFINE_TYPE (SignalDemo, signal_demo, G_TYPE_OBJECT);

 

#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer

#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer

 

void

g_cclosure_user_marshal_INT__STRING (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__STRING) (gpointer     data1,

                                                  gpointer     arg_1,

                                                  gpointer     data2);

        register GMarshalFunc_INT__STRING 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 == 2);

         

        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__STRING) (marshal_data ? marshal_data : cc->callback);

         

        v_return = callback (data1,

                             g_marshal_value_peek_string (param_values + 1),

                             data2);

         

        g_value_set_int (return_value, v_return);

}

 

 

gboolean

signal_demo_accumulator (GSignalInvocationHint *ihint,

                         GValue *return_accu,

                         const GValue *handler_return,

                         gpointer data)

{

        g_print ("%d\n", g_value_get_int (handler_return));

        g_print ("%s\n", (gchar *)data);

         

        return TRUE;

}

 

static gint

signal_demo_default_handler (gpointer instance, const gchar *buffer, gpointer userdata)

{

        g_printf ("Default handler said: %s\n", buffer);

        return 2;

}

 

static void

signal_demo_init (SignalDemo *self)

{

}

 

void

signal_demo_class_init (SignalDemoClass *klass)

{

        klass->default_handler = signal_demo_default_handler;

         

        g_signal_new ("hello",

                      G_TYPE_FROM_CLASS (klass),

                      G_SIGNAL_RUN_LAST,

                      G_STRUCT_OFFSET (SignalDemoClass, default_handler),

                      signal_demo_accumulator,

                      "haha haha",

                      g_cclosure_user_marshal_INT__STRING,

                      G_TYPE_INT,

                      1,

                      G_TYPE_STRING);

}

相对于文档 [1] 中的 signal-demo.c,上述代码主要增加了两个宏和一个函数:

1

2

3

4

#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer

#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer

 

void g_cclosure_user_marshal_INT__STRING ( ... );

这些代码不需要手工编写,我们可以使用文档 [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

/* signal_demo_class_init 函数内部 */

        g_signal_new ("hello",

                      G_TYPE_FROM_CLASS (klass),

                      G_SIGNAL_RUN_LAST,

                      G_STRUCT_OFFSET (SignalDemoClass, default_handler),

                      signal_demo_accumulator,

                      NULL,

                      g_cclosure_user_marshal_INT__STRING,

                      G_TYPE_INT,

                      1,

                      G_TYPE_STRING);

之所以替换文档 [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

#include "signal-demo.h"

  

static gint

my_signal_handler (gpointer *instance, gchar *buffer, gpointer userdata)

{

        g_print ("my_signal_handler said: %s\n", buffer);

        g_print ("my_signal_handler said: %s\n", (gchar *)userdata);

 

    return 1;

}

  

int

main (void)

{

        g_type_init ();

         

        gchar *userdata = "This is userdata";

        SignalDemo *sd_obj = g_object_new (SIGNAL_TYPE_DEMO, NULL);

         

        gint val = 0;

         

        /* 信号连接 */

        g_signal_connect (sd_obj, "hello",

                          G_CALLBACK (my_signal_handler),

                          userdata);

  

        /* 发射信号 */

        g_signal_emit_by_name (sd_obj,

                               "hello",

                               "This is the second param", &val);

 

        return 0;

}

相对于文档 [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

$ gcc signal-demo.c main.c -o test $(pkg-config --cflags --libs gobject-2.0)

 

$ ./test

my_signal_handler said: This is the second param

my_signal_handler said: This is userdata

1

haha haha

Default handler said: This is the second param

2

haha haha

根据 test 程序的输出结果去印证上述的程序源码,信号 accumulator 的工作背景及其过程便昭然在目。

参考文档

[1] GObject 信号机制『番外篇』(1)

[2] 函数指针、回调函数与 GObject 闭包

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值