参考:GObject – 2.0: Type System Concepts
Type System Concepts
The GLib Dynamic Type System
在GLib中,类型的概念比通常所理解的Ojbect type更宽泛。我们将对新类型注册到类型系统时使用的函数及数据结构进行了解,来对此进行说明。
typedef struct _GTypeInfo GTypeInfo;
struct _GTypeInfo
{
/* interface types, classed types, instantiated types */
guint16 class_size;
GBaseInitFunc base_init;
GBaseFinalizeFunc base_finalize;
/* classed types, instantiated types */
GClassInitFunc class_init;
GClassFinalizeFunc class_finalize;
gconstpointer class_data;
/* instantiated types */
guint16 instance_size;
guint16 n_preallocs;
GInstanceInitFunc instance_init;
/* value handling */
const GTypeValueTable *value_table;
};
GType
g_type_register_static (GType parent_type,
const gchar *type_name,
const GTypeInfo *info,
GTypeFlags flags);
GType
g_type_register_fundamental (GType type_id,
const gchar *type_name,
const GTypeInfo *info,
const GTypeFundamentalInfo *finfo,
GTypeFlags flags);
上面的函数用于将新的类型(GType)注册到类型系统。通常,我们使用的是g_type_register_static。基础类型属于最顶层的类型,他们不由任何类型衍生而来,这意味着非基础类型都是由其他类型衍生而来。
不管是基础类型还是非基础类型,它们都是由GTypeInfo来定义的。这也说明了类型概念在GLiib中是要比GObject type更宽。
下面将对GTypeInfo的成员进行说明。
- class_size:类所占空间大小,对于GOjbect来说,此值就是sizeof(GObjectClass)
- base_init、class_init:类初始化函数,相当于C++中的构造函数。
- base_finalize、class_finalize:类销毁函数。相当于C++中的析构函数。
- instance_size:实例所占空间大小,对于GObject来说,此值就是sizeof(GObject).
- n_preallocs:initialization policy, 相当于C++ type of new opeartor.
- value_table: 复制函数,相当于C++ copy opeartors.
Copy functions
对于GLib的所有类型来说,它们的主要的共同点是,它们都能通过简单的API来进行复制。
比如说,GValue做为所有类型的一个抽象容器。它的简单函数g_value_cope能够获取到类型的value_table,并进行GVaule到GValue的内容复制。
下面举例说明:
static void test_int (void)
{
GValue a_value = G_VALUE_INIT;
GValue b_value = G_VALUE_INIT;
guint64 a, b;
a = 0xdeadbeef;
g_value_init (&a_value, G_TYPE_UINT64);
g_value_set_uint64 (&a_value, a);
g_value_init (&b_value, G_TYPE_UINT64);
g_value_copy (&a_value, &b_value);
b = g_value_get_uint64 (&b_value);
if (a == b) {
g_print ("Yay !! 10 lines of code to copy around a uint64.\n");
} else {
g_print ("Are you sure this is not a Z80 ?\n");
}
}
static void test_object (void)
{
GObject *obj;
GValue obj_vala = G_VALUE_INIT;
GValue obj_valb = G_VALUE_INIT;
obj = g_object_new (VIEWER_TYPE_FILE, NULL);
g_value_init (&obj_vala, VIEWER_TYPE_FILE);
g_value_set_object (&obj_vala, obj);
g_value_init (&obj_valb, G_TYPE_OBJECT);
/* g_value_copy's semantics for G_TYPE_OBJECT types is to copy the reference.
* This function thus calls g_object_ref.
* It is interesting to note that the assignment works here because
* VIEWER_TYPE_FILE is a G_TYPE_OBJECT.
*/
g_value_copy (&obj_vala, &obj_valb);
g_object_unref (G_OBJECT (obj));
g_object_unref (G_OBJECT (obj));
}
Conventions
下面我们介绍几个常用宏及其作用:
- PREFIX_TYPE_OBJECT:返回相关对象的GType。例如,GTK_TYPE_WIDGET。
- PREFIX_OBJECT(obj):将obj转换为PrefixObject.例如,GTK_WIDGET(widget)。
- PREFIX_OBJECT_CLASS(klass):将klass转换为PrefixObjectClass。例如,GTK_WIDGET_CLASS。
- PREFIX_IS_OBJECT(obj):判断obj是否为OBJECT类型,并返回一个布尔值。例如,GTK_IS_WIDGE。
- PREFIX_IS_OBJECT_CLASS(klass):判断klass是否是OBJECT类型的类。并返回一个布尔值。例如,GTK_IS_WIDGE_CLASS。
- PREFIX_OBJECT_GET_CLASS(obj):返回obj实例所对应 的类指针。
Non-instantiatable non-classed fundamental types
很多类型既不能被类型系统实例化也没有所属的类。它们中大部分都是基础类型,比如说gchar.它们由GLib注册。
那,如果我们要生成一个这样的类型,咋办呢?我们先来看看glib中gchar是怎么注册的。
/* --- type initialization --- */
void
_g_value_types_init (void)
{
GTypeInfo info = {
0, /* class_size */
NULL, /* base_init */
NULL, /* base_destroy */
NULL, /* class_init */
NULL, /* class_destroy */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL, /* instance_init */
NULL, /* value_table */
};
const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
GType type G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
/* G_TYPE_CHAR / G_TYPE_UCHAR
*/
{
static const GTypeValueTable value_table = {
value_init_long0, /* value_init */
NULL, /* value_free */
value_copy_long0, /* value_copy */
NULL, /* value_peek_pointer */
"i", /* collect_format */
value_collect_int, /* collect_value */
"p", /* lcopy_format */
value_lcopy_char, /* lcopy_value */
};
info.value_table = &value_table;
type = g_type_register_fundamental (G_TYPE_CHAR, g_intern_static_string ("gchar"), &info, &finfo, 0);
g_assert (type == G_TYPE_CHAR);
type = g_type_register_fundamental (G_TYPE_UCHAR, g_intern_static_string ("guchar"), &info, &finfo, 0);
g_assert (type == G_TYPE_UCHAR);
}
...
}
问题来了,不能实例化的类型有啥用?通常,它们需要与GValue一起使用。GValue在初始化时,通常都需要整数或者字符串。这点可以从GValue的定义看出来。
struct _GValue
{
/*< private >*/
GType g_type;
/* public for GTypeValueTable methods */
union {
gint v_int;
guint v_uint;
glong v_long;
gulong v_ulong;
gint64 v_int64;
guint64 v_uint64;
gfloat v_float;
gdouble v_double;
gpointer v_pointer;
} data[2];
};
Instantiatable classed types: objects
通过类进行注册并能被实例化的类型,看起来最像是一个对象。尽管GObject是其中最为人所知的,然而,其他类似的,能用作继承结构基类的对象,也已经在Glib库外被开发出来。这里我们所说的可以作为基类的意思是,其他类能继承自此类,并不是说,我们以下面这种方式生成的类与GObject的地位完全相同。GObject的功能更宽,我们现在所说的这些对象都继承自基类。这种类型也是GLib的使用者大部分情况下都会使用的。
下面举例说明如何注册此类的对象类型。
typedef struct {
GObject parent_instance;
/* instance members */
char *filename;
} ViewerFile;
typedef struct {
GObjectClass parent_class;
/* class members */
/* the first is public, pure and virtual */
void (*open) (ViewerFile *self,
GError **error);
/* the second is public and virtual */
void (*close) (ViewerFile *self,
GError **error);
} ViewerFileClass;
#define VIEWER_TYPE_FILE (viewer_file_get_type ())
GType
viewer_file_get_type (void)
{
static GType type = 0;
if (type == 0) {
const GTypeInfo info = {
.class_size = sizeof (ViewerFileClass),
.base_init = NULL,
.base_finalize = NULL,
.class_init = (GClassInitFunc) viewer_file_class_init,
.class_finalize = NULL,
.class_data = NULL,
.instance_size = sizeof (ViewerFile),
.n_preallocs = 0,
.instance_init = (GInstanceInitFunc) viewer_file_init,
};
type = g_type_register_static (G_TYPE_OBJECT,
"ViewerFile",
&info, 0);
}
return type;
}