Gobject tutorial 二

Type system and registration process

Gobject本身是一个基础对象类型。我们平常并不会直接使用。通常我们都是使用其衍生类型,比如,各种类型的GtkWidget。

本次,我们聊聊如何创建一个Gobject的衍生对象。我们的例子是实现一个表示数字的对象。尽管c语言中有int、double等类型来表示数字。然而我们的例子与他们并不冲突。对此,我们需要从更抽象的层次来理解。如果你去了解抽象代数,或许你会对此有更深刻的认识。

Name convention

首先,我们来了解一下命名规则。对象类型的名称包含名字空间和对象名字两部分。比如说,Gobject包含名字空间G和对象名字Object,GtkWidget包含名字空间Gtk和对象名字Widget。在我们的例子中,我们使用T作为名字空间,Double作为对象名字。TDouble就是我们构建的新的类型的名字。它是由GObject衍生而来,它代表一个真实的数字,一个真实的阿拉伯数字,数字的类型就是c语言中的double。

Define TDoubleClass and TDouble

我们的新类型TDoube,包含TDouble类和TDouble实例。因此,对于一个完整的TDouble类型,我们要分两部分进行定义。

对于TDouble的类,以C语言的形式进行表示时,它的名字是TDoubleClass,定义如下:

typedef struct _TDoubleClass TDoubleClass;
struct _TDoubleClass {
  GObjectClass parent_class;
};

我们要说明的是,表示TDoubleClass的结构体的第一个成员必须是其父类结构体。那么,问题来了,为啥这么做呢?如果去研究GTK源码,你会发现其中有很多强制类型转化来简化获取结构体成员的操作。此处这么定义就能说明为什么可以做强制类型转换。

对于TDouble的实例,以C语言的形式进行表示时,它的名字是TDouble,定义如下:

typedef struct _TDouble TDouble;
struct _TDouble {
  GObject parent;
  double value;
};

同理, 表示TDouble的结构体的第一个成员必须是其父实例结构体。原因同上。

Creation process of a child of GObject

新类型TDouble的创建包含以下几个步骤:

  1. 将TDouble类型注册到类型系统中。
  2. 类型系统为TDoubleClass 和TDouble分配内存空间。
  3. 初始化TDoubleClass。
  4. 初始化TDouble。

Registration

对于注册本身来说,分为静态注册和动态注册两种。两者的区别在于,即使特定类型的所有实例都被销毁了,静态注册的类型也不会销毁其类。而动态注册的类型在其最后一个实例销毁时,其类也会被销毁。值得注意的是,GObject是静态注册的,同时,它的衍生对象类型静态注册的。静态注册的函数如下:

GType
g_type_register_static (GType           parent_type,
                        const gchar     *type_name,
                        const GTypeInfo *info,
                        GTypeFlags      flags);

GTypeInfo结构体,正如其名,它存放的是关于对象类型的信息。其定义如下:

typedef struct _GTypeInfo  GTypeInfo;

struct _GTypeInfo
{
  /* interface types, classed types, instantiated types */
//用于表示类结构体的大小,对于Tdouble,此值就是siezof(TDoubleClass)
  guint16                class_size;
//下边两个成员用于initialize/finalize类的动态成员
  GBaseInitFunc          base_init;
  GBaseFinalizeFunc      base_finalize;

  /* interface types, classed types, instantiated types */
  GClassInitFunc         class_init; //初始化类的静态成员。
  GClassFinalizeFunc     class_finalize; //finalize class
  gconstpointer          class_data; //用户提供的参数,供上述两函数使用

  /* instantiated types */
  guint16                instance_size; //实例的大小,对于TDouble,此值就是sizeoof(TDouble)
  guint16                n_preallocs;//ignored
  GInstanceInitFunc      instance_init; //初始化实例成员

  /* value handling */
  const GTypeValueTable  *value_table; //对基类有用,对于GObject的衍生类,无用。
};

下面举例说明,一个完成的注册新类型的流程。

 1 #include <glib-object.h>
 2 
 3 #define T_TYPE_DOUBLE  (t_double_get_type ())
 4 
 5 typedef struct _TDouble TDouble;
 6 struct _TDouble {
 7   GObject parent;
 8   double value;
 9 };
10 
11 typedef struct _TDoubleClass TDoubleClass;
12 struct _TDoubleClass {
13   GObjectClass parent_class;
14 };
15 
16 static void
17 t_double_class_init (TDoubleClass *class) {
18 }
19 
20 static void
21 t_double_init (TDouble *self) {
22 }
23 /*此函数返回TDouble对象的类型,对于此功能的函数命名,有个规则,那就是<name space>_<name>_get_type ,为了更方便使用其返回值,我们定义了一个宏来指代其返回值。
这个宏也是有命名规则的。即<NAME_SPACE>_TYPE_<NAME> */
24 GType
25 t_double_get_type (void) {
26   static GType type = 0;
27   GTypeInfo info;
28 
29   if (type == 0) {
30     info.class_size = sizeof (TDoubleClass);
31     info.base_init = NULL;
32     info.base_finalize = NULL;
33     info.class_init = (GClassInitFunc)  t_double_class_init;
34     info.class_finalize = NULL;
35     info.class_data = NULL;
36     info.instance_size = sizeof (TDouble);
37     info.n_preallocs = 0;
38     info.instance_init = (GInstanceInitFunc)  t_double_init;
39     info.value_table = NULL;
40     type = g_type_register_static (G_TYPE_OBJECT, "TDouble", &info, 0);
41   }
42   return type;
43 }
44 
45 int
46 main (int argc, char **argv) {
47   GType dtype;
48   TDouble *d;
49 
50   dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
51   if (dtype)
52     g_print ("Registration was a success. The type is %lx.\n", dtype);
53   else
54     g_print ("Registration failed.\n");
55 
56   d = g_object_new (T_TYPE_DOUBLE, NULL);
57   if (d)
58     g_print ("Instantiation was a success. The instance address is %p.\n", d);
59   else
60     g_print ("Instantiation failed.\n");
61   g_object_unref (d); /* Releases the object d. */
62 
63   return 0;
64 }
65 

G_DEFINE_TYPE macro 

上述的注册套路是固定的。因此,可以定义宏来简化编程。这个宏就是G_DEFINE_TYPE。

G_DEFINE_TYPE宏的主要工作如下:

  • 声明类初始化函数,函数命名规则为<name space>_<name>_class_init。对于TDouble来说,这个函数就叫做t_double_class_init。此处只负责声明,实现部分由用户负责。
  • 声明实例初始化函数,函数命名规则为<name space>_<name>_init。对于TDouble来说,这个函数名就是t_double_init。同样的,此处只负责声明,实现部分由用户负责。
  • 定义一个静态变量,此变量指向其父类。这个变量的命名规则为<name space>_<name>_parent_class。对于TDouble来说,这个变量名就是t_double_parent_class。
  • 定义了一个<name space>_<name>_get_type函数。对于TDouble来说,就是定义了t_doule_get_type.对于使用此宏的对象类型来说,已经完成对对象类型的注册。

使用此宏,上述例子可以写成这样。

#include <glib-object.h>

#define T_TYPE_DOUBLE  (t_double_get_type ())

typedef struct _TDouble TDouble;
struct _TDouble {
  GObject parent;
  double value;
};

typedef struct _TDoubleClass TDoubleClass;
struct _TDoubleClass {
  GObjectClass parent_class;
};

G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)

static void
t_double_class_init (TDoubleClass *class) {
}

static void
t_double_init (TDouble *self) {
}

int
main (int argc, char **argv) {
  GType dtype;
  TDouble *d;

  dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
  if (dtype)
    g_print ("Registration was a success. The type is %lx.\n", dtype);
  else
    g_print ("Registration failed.\n");

  d = g_object_new (T_TYPE_DOUBLE, NULL);
  if (d)
    g_print ("Instantiation was a success. The instance address is %p.\n", d);
  else
    g_print ("Instantiation failed.\n");
  g_object_unref (d);

  return 0;
}

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值