1 编写SignalDemo类
/* file name : signal-demo.h */
#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
/* file name : signal-demo.c */
#include "signal-demo.h"
G_DEFINE_TYPE (SignalDemo, signal_demo, G_TYPE_OBJECT);
static void
signal_demo_default_handler(gpointer instance, const gchar *buffer, gpointer userdata)
{
g_printf("Default handler said: %s\n", buffer);
}
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_FIRST,
G_STRUCT_OFFSET (SignalDemoClass, default_handler),
NULL,
NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
1,
G_TYPE_STRING);
}
/* file name : main.c */
#include "signal-demo.h"
static void
my_signal_handler(gpointer *instance, gchar *buffer, gpointer userdata)
{
g_printf ("my_signal_handler said: %s\n", buffer);
g_printf ("my_signal_handler said: %s\n", (gchar *)userdata);
}
int
main(void)
{
g_type_init();
gchar *userdata = "This is userdata";
SignalDemo *sd_obj = g_object_new(SIGNAL_TYPE_DEMO, NULL);
/* 信号连接 */
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",
G_TYPE_NONE);
return 0;
}
2 分析 g_signal_new 函数的参数
- Param 1 :字符串"hello",它表示信号
- Param 2:SignalDemo类的类型ID,可以使用 G_TYPE_FROM_CLASS 宏从 SignalDemoClass 结构体中获取,也可直接使用 signal-demo.h 中定义的宏 SIGNAL_TYPE_DEMO。
- Param 3:两个闭包的先把运行顺序。
- Param 4:它是一个内存偏移量,主要用于从 SignalDemoClass 结构体中找到 default_handler 指针的位置,可以使用 G_STRUCT_OFFSET 宏来获取,也可以直接根据 signal-demo.h 中的 SignalDemoClass 结构体的定义,使用 sizeof (GObjectClass) 来得到内存偏移量,因为 default_handler 指针之前只有一个 GObjectClass 结构体成员。
- Param 5:NULL
- Param 6:NULL
- Param 7:事实上 marshal 主要是用来“翻译”闭包的参数和返回值类型的,它将翻译的结果传递给闭包。之所以不直接调用闭包,而是在其外加了一层 marshal 的包装,主要是方便 GObject 库与其他语言的绑定。例如,我们可以写一个 pyg_closure_marshal_VOID__STRING 函数,其中可以调用 python 语言编写的“闭包”并将其计算结果传递给 GValue 容器,然后再从 GValue 容器中提取计算结果。
- Param 8:marshal 函数的返回值类型。由于本例的第 7 个参数所指定的 marshal 是 g_cclosure_marshal_VOID__STRING 函数的返回值是 void,而 void 类型在 GObject 库的类型管理系统是 G_TYPE_NONE 类型。
- Param 9:指定 g_signal_new 函数向 marshal 函数传递的参数个数,由于本例使用的 marshal 函数是 g_cclosure_marshal_VOID__STRING 函数,g_signal_new 函数只向其传递 1 个参数。
- Param 10:是可变参数,其数量由第 8 个参数决定,用于指定 g_signal_new 函数向 marshal 函数传递的参数类型。由于本例使用的 marshal 函数是 g_cclosure_marshal_VOID__STRING 函数,并且 g_signal_new 函数只向其传递一个参数,所以传入的参数类型为 G_TYPE_STRING(GObject 库类型管理系统中的字符串类型)。
3 g_cclosure_marshal_VOID__STRING约定的回调函数
g_cclosure_marshal_VOID__STRING是g_signal_new的参数。需要注意,g_cclosure_marshal_VOID__STRING 所约定的回调函数类型为:
void (*callback) (gpointer instance, const gchar *arg1, gpointer user_data)
- Param 1:信号的默认闭包(信号注册阶段出现)和信号使用者提供的闭包(信号连接阶段出现)所必须的,但是这个参数是隐式存在的,由g_signal_new暗自向闭包传递。
- Param 2:参数是显式的,同时也是信号的默认闭包和信号使用者提供的闭包所必须的,这个参数由信号的发射函数(例如 g_signal_emit_by_name)向闭包传递,本文中是”This is the second param"。
- Param 3:第 3 个参数也是显式的,且只被信号使用者提供的闭包所关注,这个参数由信号的连接函数(例如 g_signal_connect)向闭包传递
- gchar *userdata = “This is userdata”;。
总的来说,发射信号后会运行两个闭包,一个是用户自定义的闭包(通过g_signal_connect传入的闭包),另一个是新建信号的时候传入的闭包(g_signal_new传入的闭包)。
闭包的第二和第三个参数分别是g_signal_emit_by_name的第三个参数和g_signal_connect第三个参数传入。