发布者: ouyang

glib version : 2.36.4

文章结构

本文首先介绍GType,及其相关知识。随后用一个例子演示了如何使用GType。

在下一篇中,将从源码角度分析GType如何实现封装,继承,多态。

什么是GType ?

GLib是一个跨平台的GTK的支撑库,它提供了多种高级的数据结构,如内存块、双向和单向链表、哈希表、动态字符串等。

Gobject是glib提供的一个轻便的对象系统,而gtype为它提供了一个动态的运行时面向对象的类型系统。

什么是GType 支持什么样的系统?

多层次、单根继承的类型系统。

它的对象由两部分组成:类结构和实例结构。

类结构唯一,它为该对象所有实例的共有部分;实例结构可以有多个,它是对象的具体实现。

或者说:实例里面保存了数据,而类主要保存了操作数据的方法。或者说类是一个类的所有对象公有的,而实例结构是一个实例特有的。

至于它为什么要这样设计,可以参考这里:GObject设计思想 。

怎样使用GType ?

GType支撑三种类型:

  1. 基本类型,通过g_type_register_fundamental 来注册,它需要的信息由GTypeInfo和GTypeFundamentalInfo传入。比如gobject里面的GObject就是一个基本类型。由于大部分的基本类型都已经预先注册好了,基本上不用关系该类型的使用。

  2. 静态类型,g_type_register_static来注册,其类型信息由GTypeInfo传入。GTK里面的各种窗口和容器类型都是通过这种方式注册的。

  3. 动态类型,g_type_register_dynamic注册,其类型信息由GTypePlugin传入。之所谓分为动态和静态,是因为动态的类型可以在运行时加载和卸载。

cloverprince的一系列博客从简到繁介绍怎么样使用GType注册我们的类。看完这些例子,相信也会加深对GType为什么要这样设计的理解。

从例子出发

在接下来的例子中,我们将注册一个基本类型和一个静态类型。

首先注册一个基本类型,我们把它叫做大猩猩。我们定义它的类结构和实例结构:

typedef struct {
  GTypeInstance g_type_instance;
 
 /*< private >*/
 int m_privateInt;
} OrangutanObject;
 
typedef struct {
 GTypeClass g_type_class;
 
 void  (*create) ();
} OranObjectClass;
 
void oran_object_create() {
  printf("Give birth to a new Orangutan\n");
}

在实例结构中,第一个数据是固定的父类的Instace结构。接下来是一些私有数据。

在类结构中,我们定义了Orangutan的构造函数形式。类结构的第一个数据必须是父类的Class结构,它是该类型的一个标识。

另外,我们定义了一个构造函数。

接下来, 注册基本类型需要GTypeInfo和GTypeFundamentalInfo:

static const GTypeFundamentalInfo orang_info = {
    (GTypeFundamentalFlags)(G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE |
    G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE),
};
 
 
static void OranClassInitFunc(OranObjectClass* klass, gpointer data) {
   klass->create = oran_object_create;
}
 
static void OranInstanceInitFunc(OranObject* instance, gpointer data) {
   instance->m_privateInt = 42;
}
static const GTypeInfo oran_type_info = {
    sizeof(mybaseclass_t);//class_size;
    NULL, //base_init;
    NULL, //base_finalize;
    (GClassInitFunc)OranClassInitFunc, //class_init;
    NULL, //class_finalize;
    NULL, //class_data;
    sizeof(mybaseinstance_t),//instance_size;
    0, //n_preallocs;
    (GInstanceInitFunc)OranInstanceInitFunc, //instance_init;
    NULL, //value_table;
};

GTypeInfo 各参数的含义注释已经很清楚了。而GTypeFundamentalInfo 可以看出其成员是一个enum,这个enum标识了要注册的类型能否实例化,能否被继承等信息。准备工作就绪后,是时候为“猩猩”安家落户了:

static GType GetOranType() = {
  static GType type = 0;
  if (type == 0) {
    type = g_type_register_fundamental (
    g_type_fundamental_next(),//A predefined type identifier.
    “OrangutanClass”,//0-terminated string used as the name of the new type.
    &oran_type_info, //GTypeInfo structure for this type.
    &orang_info, //GTypeFundamentalInfo structure for this type.
    GTypeFlags(0) //Bitwise combination of #GTypeFlags values
  );
  return type;
}

GTypeFlags 是什么呢?它也是一个enum:

typedef enum {
G_TYPE_FLAG_ABSTRACT = (1 << 4), // Indicates an abstract type. No instances can be&nbsp;created for an abstract type.
G_TYPE_FLAG_VALUE_ABSTRACT = (1 << 5) //Indicates an abstract value type
} GTypeFlags;

接下来,我们要为“orangutan”添加一个进化后的子类“human”。同样实现类结构和实例结构:

struct _HumanObject {
  OrangutanObject parent;  /* <private> */  int m_privateInt;
};
typedef _HumanObject HumanObject;
 
 
struct _HumanObjectClass {
  OranObjectClass parent_klass;};
 
typedef _HumanObjectClass HumanObjectClass;

我们需要将create方法替换一下,不能继续”Give birth to a new Orangutan”了;同时初始化GTypeInfo,并调用g_type_register_static注册我们的HumanClass,因为我们并不准备注册一个基本类型。

void human_object_create() {
    printf("Give birth to a new man!\n");
}
 
static void HumanClassInitFunc(HumanObjectClass* klass, gpointer data) {
  OranObjectClass *klass_oran = G_TYPE_CHECK_CLASS_CAST(klass, GetOranType(), OranObjectClass);
  klass_oran->create = human_object_create;
}
 
static void HumanInstanceInitFunc(HumanObject* instance, gpointer data) {
  instance->m_privateInt = 42;
}
 
static const GTypeInfo human_type_info = {
    sizeof(HumanObjectClass), //fill it with humanobject class;
    NULL, //base_init;
    NULL, //base_finalize;
    (GClassInitFunc)HumanClassInitFunc, //use human class init func;
    NULL, //class_finalize;
    NULL, //class_data;
    sizeof(HumanObject),//human size;
    0, //n_preallocs;
    (GInstanceInitFunc)HumanInstanceInitFunc, //human instance init func;
    NULL, //value_table;
};
 
GType GetHumanType() {
    static GType type = 0;
    if (type == 0) {
        type = g_type_register_static(
                GetOranType(),
                "HumanClass",
                &human_type_info,
                GTypeFlags(0)
                );
    }
    return type;
}

现在,可以测试我们的”Orangutan”以及”Human”,希望Human不要”give birth to a new orangutan” :)

int main() {
    g_type_init();
 
    printf("Orangutan Class ID: %d, name: %s\n", GetOranType(), g_type_name(GetOranType()));
 
    OrangutanObject* orangutan = (OrangutanObject*)g_type_create_instance(GetOranType());
    OranObjectClass* klass = G_TYPE_INSTANCE_GET_CLASS(orangutan, GetOranType(), OranObjectClass);
    klass->create();
 
    printf("Human Class ID: %d, name: %s\n", GetHumanType(), g_type_name(GetHumanType()));
    HumanObject* human = (HumanObject*)g_type_create_instance(GetHumanType());
    OranObjectClass* h_klass = G_TYPE_INSTANCE_GET_CLASS(human, GetHumanType(), OranObjectClass);
    h_klass->create();
 
    printf("Orangutan instance private int : %d.\n", orangutan->m_privateInt);
    printf("Human instance private int : %d.\n", human->m_privateInt);
 
    return 0;
}

上面的代码都很简单,需要注意一下的是g_type_init()这个函数。使用g_type之前,需要调用它。它会做一些必要的初始化工作(注册一些基本类型等)。

看看输出:

Orangutan Class ID: 196, name: OrangutanClass
Give birth to a new Orangutan
Human Class ID: 5277952, name: HumanClass
Give birth to a new man!
Orangutan instance private int : 0.
Human instance private int : 42.

一切正如我们的预期。

需要说明的是,这仅仅是一个简单的例子。更复杂的例子可以参考上面两节提到的GObject设计思想和cloverprince的一系列博客。或者gobject的源码(后续将会介绍)。

疑问

这个简单的例子介绍了 gtype的简单用法,也带来更多的疑问:

  1. GType 是什么?是什么类型?

  2. GTypeInstance, GTypeClass是什么?

  3. GTypeInfo的value_table是什么?它的用途是什么?

  4. 虚函数是怎么实现的?

  5. 怎么处理多层继承的情况?怎么实现虚继承?怎么写接口类?

。。。

这些问题,都将在下一篇 GType类型系统(下)中解答



-EOF-
睿初科技软件开发技术博客,转载请注明出处

blog comments powered by Disqus