初创建于: 2022-09-11 22:34
GObject
概述
GObject 可以理解为一个库, 使用这个库可以用 C 语言编写面向对象的程序.
这里通过一个例子直观地来理解一下 GObject.
定义类
使用 GObject 时, 变量的命名尽量遵循 <module>_<type>
的前缀, 如当前工程名为 T
, 要定义一个类 Integer
, 则将该类命名为 TInteger
, 并对于各种函数以前缀 t_integer
命名.
首先, 在 GObject 中, 要定义一个「Class」需要两个结构体, 比如, 我要定义一个 TInteger
类, 则需要定义一个结构体 typedef struct _TInteger TInteger
与 typedef struct _TIntegerClass TIntegerClass
.
这两者之间的关系比较复杂, 能力有限, 我难以描述清楚. 这里我们只关注如何使用, 而不深究其原理.
typedef _IntegerClass IntegerClass;
struct _IntegerClass {
// 将 GObjectClass 作为其第一个成员, 代表继承自 GObject
GObjectClass parent_class;
};
typedef _Integer Integer;
struct _Integer {
// 将 GObject 作为其第一个成员, 代表继承自 GObject
GObject parent;
// 公开的成员变量
int sex;
};
添加 private 属性
要为一个类添加 private 属性, 需要如下方法:
首先定义一个 TIntegerPrivate
类, 并将所有要设置为 private
的属性放到这里面
typedef struct _TIntegerPrivate TIntegerPrivate;
struct _TIntegerPrivate {
int value;
};
需要注意的是, GObject 系统添加 private 属性的原理大概是, 在申请每一个类实例的空间时, 额外申请一块 private 的空间并“挂”到实例的空间旁边.
GObject 系统 private 属性的大小有 64K 的限制. 这一限制在 vala 中也有, 因为实际上 vala 编译器就是将 vala 代码“编译”为使用 glib 等实现的 C 语言代码. 因此 vala 中私有属性也无法避免 64K 大小的限制.
注册类型
无论是否添加 private 属性, 在定义了 TInteger
, TIntegerClass
以及可能还有 TIntegerPrivate
之后, 都需要向 GObject 系统注册该类.
首先, 需要定义宏:
#define T_TYPE_INTEGER (t_integer_get_type())
如果没有定义 private 属性, 则可以:
// G_TYPE_OBJECT 与 T_TYPE_INTEGER 类似, 是属于 Integer 类父类的
G_DEFINE_TYPE (TInteger, t_integer, G_TYPE_OBJECT);
如果定义了 private 属性, 则可以:
G_DEFINE_TYPE_WITH_CODE (TInteger, t_integer, G_TYPE_OBJECT, G_ADD_PRIVATE(TInteger));
该宏会定义一个函数 TIntegerPrivate* t_integer_get_instance_private(TInteger*);
, 该函数用于取出其私有属性.
通常将该部分内容写到一个单独的 c 文件中, 来对外隐藏 private 的部分.
初始化
在注册类型之后, G_DEFINE_TYPE*
宏会声明两个函数 : static void t_integer_init(TInteger*);
与 static void t_integer_class_init(TIntegerClass*);
static void t_integer_init(TInteger* i) {
TIntegerPrivate priv = t_integer_get_instance_private(i);
priv->value = 0;
// 如果没有 private, 则直接
// i->value = 0;
}
一些特殊的宏
G_DECLARE_FINAL_TYPE
G_DECLARE_FINAL_TYPE (ModuleObjName, module_obj_name, MODULE, OBJ_NAME, ParentName)
/**
* Example:
* G_DECLARE_FINAL_TYPE (TDouble, t_double, T, Double, GObject);
*/
宏 G_DECLARE_FINAL_TYPE
会做以下事情:
- 定义函数
<module>_<objName>_get_type()
. 如t_double_get_type()
. - 声明了类型
typedef struct _<module><objName> <module><objName>
及typedef struct _<module><objName>Class <module><objName>Class
. 如typedef struct _TDouble TDouble
和struct _TDoubleClass TDoubleClass
. - 定义宏
<module>_<objName>
. 如T_DOUBLE
. 该宏会被展开成为一个函数, 该函数将一个gpointer*
强制转换为TDouble*
. - 定义宏
<module>_IS_<objName>
. 如T_IS_DOUBLE
.
与 G_DECLARE_FINAL_TYPE
类似的还有 G_DECLARE_DERIVABLE_TYPE
, 其区别在于 G_DECLARE_FINAL_TYPE
定义的类不可被继承, 而 G_DECLARE_DERIVABLE_TYPE
声明的类可以被继承, 即再产生子类.
G_DECLARE_*_TYPE
类宏与 G_DEFINE_TYPE*
类宏的区别在于, DECLARE
只是做了一些“体力活”, 即将一部分同质化的函数通过宏定义出来, 如 T_IS_INTEGER
等, 但是并没有向 GObject 系统注册该类, 而 DEFINE
类宏是向 GObject 类注册了该类.
一个例子
这里以 TInteger
作为一个例子.
t_integer.h :
#include <glib-object.h>
#ifndef __T_INTEGER_H_UCRSVKFT
#define __T_INTEGER_H_UCRSVKFT
#define T_TYPE_INTEGER (t_integer_get_type())
G_DECLARE_DERIVABLE_TYPE(TInteger, t_integer, T, INTEGER, GObject);
struct _TIntegerClass {
GObjectClass parent_class;
};
TInteger*
t_integer_new(int value);
gboolean
t_integer_get_value(TInteger *self, int* value);
gboolean
t_integer_set_value(TInteger *self, int value);
#endif /* end of include guard: __T_INTEGER_H_UCRSVKFT */
t_integer.c :
#include "t_integer.h"
typedef struct _TIntegerPrivate TIntegerPrivate;
struct _TIntegerPrivate {
int value;
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(TInteger, t_integer, G_TYPE_OBJECT);
static void
t_integer_class_init(TIntegerClass* klass) { }
static void
t_integer_init(TInteger* self) {
TIntegerPrivate* priv = t_integer_get_instance_private(self);
priv->value = 0;
}
TInteger*
t_integer_new(int value) {
TInteger* t_i = g_object_new(T_TYPE_INTEGER, NULL);
TIntegerPrivate* priv = t_integer_get_instance_private(t_i);
priv->value = value;
return t_i;
}
gboolean
t_integer_get_value(TInteger *self, int* value) {
g_return_val_if_fail(T_IS_INTEGER(self), FALSE);
TIntegerPrivate* priv = t_integer_get_instance_private(self);
*value = priv->value;
return TRUE;
}
gboolean
t_integer_set_value(TInteger *self, int value) {
g_return_val_if_fail(T_IS_INTEGER(self), FALSE);
TIntegerPrivate* priv = t_integer_get_instance_private(self);
priv->value = value;
return TRUE;
}
信号机制
信号机制有点像 java 中的 ActionListener
. 通过信号机制, 可以为某个实例注册一个信号, 当触发该信号时, 可以调用相应的回调函数.
信号注册 (Signal Registration)
在使用一个信号之前需先使用 g_signal_new
函数注册该信号:
guint g_signal_new(
const gchar *signal_name , /* 信号名 */
GType itype , /* 要为哪个类注册信号 */
GSignalFlags signal_flags , /* 信号的 Flag */
guint class_offset , /* 一个偏移量, 设为 0 即可 */
GSignalAccumulator accumulator , /* 暂时忽略这个参数 */
gpointer accu_data , /* 暂时忽略这个参数, 设为NULL */
GSignalCMarshaller c_marshaller , /* 暂时忽略这个参数 */
GType return_type , /* handler 函数的返回类型 */
guint n_params , /* handler 函数接收的参数个数 */
... /* 如果 n_params 是 0 则不需要 */
)
例如:
#define T_INTEGER_SIGNAL_DIV_BY_ZERO "div-by-zero"
static guint t_integer_signal_div_by_zero;
static void
t_integer_class_init(TIntegerClass* klass) {
t_integer_signal_div_by_zero =
g_signal_new(
T_INTEGER_SIGNAL_DIV_BY_ZERO ,
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0
);
}
注意信号的名称有命名规则, 只能使用 ASCII 字符, 用短划线 "-" 或下划线 "_" 连接, 必须以字母开始, 建议使用短划线连接, 并且短划线与下划线不能混合使用.
信号处理 (Signal Handler)
定义如下函数:
static void callback_div_by_zero(TInteger* i, gpointer* user_data) {
g_print("[ERROR] Div by ZERO!\n");
}
信号释放 (Signal Emission)
gboolean
t_integer_div(TInteger *a, TInteger *b, TInteger* result) {
g_return_val_if_fail(T_IS_INTEGER(a) && T_IS_INTEGER(b) && T_IS_INTEGER(result), FALSE);
int va, vb;
gboolean ja = t_integer_get_value(a, &va);
gboolean jb = t_integer_get_value(b, &vb);
if (vb == 0) {
g_signal_emit(b, t_integer_signal_div_by_zero, 0);
return FALSE;
}
gboolean jr = t_integer_set_value(result, va / vb);
return ja && jb && jr ? TRUE : FALSE;
}
信号链接 (Signal Connection)
void g_signal_connect(instance, detailed_signal, c_handler, data);
如 :
TInteger*
t_integer_new(int value) {
TInteger* t_i = g_object_new(T_TYPE_INTEGER, NULL);
TIntegerPrivate* priv = t_integer_get_instance_private(t_i);
priv->value = value;
g_signal_connect(t_i, T_INTEGER_SIGNAL_DIV_BY_ZERO, G_CALLBACK( callback_div_by_zero ), NULL);
return t_i;
}
默认 Handler
有一些信号, 我们希望给他们默认的 Handler, 则可以使用 g_signal_new_class_handler
函数注册信号.
g_signal_new_class_handler
函数与 g_signal_new
函数的区别在于第四个参数, 在 g_signal_new
函数中, 第四个参数是一个偏移量, 用于在类中按照该偏移量寻找默认 handler 函数, 这样做的缺陷是对于 final 类, 无法在类中定义默认 handler 函数. 而 g_signal_new_class_handler
函数中第四个参数是一个函数, 用以指定为默认 handler.
static guint t_integer_signal_div_by_zero;
static void callback_div_by_zero(TInteger* i, gpointer* user_data);
static void
t_integer_class_init(TIntegerClass* klass) {
t_integer_signal_div_by_zero =
g_signal_new_class_handler(T_INTEGER_SIGNAL_DIV_BY_ZERO ,
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
G_CALLBACK(callback_div_by_zero),
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
}
在定义了默认 handler 后, 无需再对信号进行连接.
Flags
对于 g_signal_new
与 g_signal_new_class_handler
函数中的第 3 个参数 signal_flags
, 有如下选项:
Flag | 含义 |
---|---|
G_SIGNAL_RUN_FIRST |
默认 handler 在所有用户定义 handler 之前运行 |
G_SIGNAL_RUN_LAST |
默认 handler 在用户定义的正常 handler 之后运行 (没有被 g_signal_connect_after 连接的) |
G_SIGNAL_RUN_CLEANUP |
默认 handler 在所有用户定义 handler 和之后运行 |
参数
有时候, 对于信号的 handler 函数, 需要传递一些参数.
标签:INTEGER,signal,value,Note,integer,GObject,TInteger,TYPE From: https://www.cnblogs.com/Corona09/p/17038865.html