注:之前曾写过一篇类似文章,结果被我误删了
按照GObject手册的说法,Boxed Type机制是用来包装(wrap)C语言结构体的一种机制。这里所谓的包装,其实就是在GType类型系统中注册该类型,并且该类型将成为GBoxed的子类。之后,这一类型就能够使用和GBoxed类型有关的所有函数了,如g_value_get_boxed等等。
首先,我们看一下GBoxed在GType类型系统中的“注册信息”:
void g_boxed_type_init (void) { static const 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_TYPE_BOXED */ type = g_type_register_fundamental (G_TYPE_BOXED, g_intern_static_string ("GBoxed"), &info, &finfo, G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT); g_assert (type == G_TYPE_BOXED); }
从以上信息中,我们可以看出,GBoxed是一个基本类型,不可实例化(instantiable)、不可类化(classed),可以被继承,但不可以被深继承(即继承了GBoxed的子类不能再被继承)。
要想在GType类型系统中注册一个Boxed Type,我们需要提供一个copy_func和一个free_func,从名字上也可以看出,前者负责该类型的拷贝,后者负责该类型的释放。
为更方便地说明问题,我们假设我们需要包装以下的StudentInfo类型:
typedef struct _StudentInfo StudentInfo; struct _StudentInfo { gint number; GString *name; gdouble grade; };
为了包装该类型,我们自然需要提供一个copy_func和一个free_func:
StudentInfo *student_info_new(gint number,gchar *name,gdouble grade) { StudentInfo *new_std; new_std=g_new(StudentInfo,1); new_std->number=number; new_std->name=g_string_new(name); new_std->grade=grade; return new_std; } StudentInfo *student_info_copy(const StudentInfo *std_info) { StudentInfo *new_std; new_std=g_new(StudentInfo,1); new_std->number=std_info->number; new_std->name=g_string_new(std_info->name->str); new_std->grade=std_info->grade; return new_std; }
下面,我们需要一个student_info_type_init函数来承担运行时刻注册该类型的任务。为方便起见,我们也为该函数配套了一个宏:
GType student_info_get_type() { static GType student_info_id=0; if (student_info_id==0) { student_info_id=g_boxed_type_register_static("StudentInfo",(GBoxedCopyFunc)student_info_copy,(GBoxedFreeFunc)student_info_free); } return student_info_id; } #define TYPE_STUDENT_INFO (student_info_get_type())
从上面的代码中可以看出,Boxed Type的注册工作实质上是由g_boxed_type_register_static函数完成的,该函数原型如下:
GType g_boxed_type_register_static (const gchar *name, GBoxedCopyFunc boxed_copy, GBoxedFreeFunc boxed_free);
该函数有三个参数,其中name是Boxed Type的名字,GBoxedCopyFunc和GBoxedFreeFunc则是用typedef定义的函数指针类型:
typedef gpointer (*GBoxedCopyFunc) (gpointer boxed); typedef void (*GBoxedFreeFunc) (gpointer boxed);
至此,包装工作就完成了。
但是上面给出的student_info_get_type()函数有一个问题:它不是线程安全的。而且,每次写这么多也显得很臃肿,为此,我们可以使用一个宏G_DEFINE_BOXED_TYPE:
#define G_DEFINE_BOXED_TYPE(TypeName, type_name, copy_func, free_func) G_DEFINE_BOXED_TYPE_WITH_CODE (TypeName, type_name, copy_func, free_func, {})
该宏有4个参数:
- TypeName: 该参数是该Boxed Type的名字,且不用引号包围。
- type_name: 该参数将成为_get_type()的前缀,即最终自动生成的函数的名字为type_name_get_type()
-
copy_func: 该参数为函数指针或函数名(函数指针常量?),其类型应为以下三种之一:
typedef gpointer (*copy_func) (gpointer boxed); typedef TypeName *(*copy_func) (const TypeName *boxed); typedef TypeName *(*copy_func) (TypeName *boxed);
-
free_func: 该参数为函数指针或函数名(函数指针常量?),其类型应为以下两种之一:
typedef void (*free_func) (gpointer boxed); typedef void (*free_func) (TypeName *boxed);
因此,如果想生成student_info_get_type()函数的话,只要按如下所示填写参数:
G_DEFINE_BOXED_TYPE(StudentInfo,student_info,student_info_copy,student_info_free);
如果你由于种种原因而需要自己写*_get_type()函数的话,为使其线程安全,可以将以下代码作为模板使用:
GType g_strv_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; if (g_once_init_enter (&g_define_type_id__volatile)) { GType g_define_type_id = g_boxed_type_register_static (g_intern_static_string ("GStrv"), (GBoxedCopyFunc) g_strdupv, (GBoxedFreeFunc) g_strfreev); g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); } return g_define_type_id__volatile; }
事实上,很久以前,G_DEFINE_BOXED_TYPE展开以后,也是和上面的代码类似的——之所以要改用现在的写法,主要还是为了增强对类型的检测。
完整的测试代码如下:
#include <glib.h> #include <glib-object.h> #include <stdlib.h> typedef struct _StudentInfo StudentInfo; struct _StudentInfo { gint number; GString *name; gdouble grade; }; StudentInfo *student_info_new(gint number,gchar *name,gdouble grade) { StudentInfo *new_std; new_std=g_new(StudentInfo,1); new_std->number=number; new_std->name=g_string_new(name); new_std->grade=grade; return new_std; } void student_info_print(const StudentInfo *std_info) { g_print("Number:%3d Name:%5s Grade:%3.1f",std_info->number,std_info->name->str,std_info->grade); } StudentInfo *student_info_copy(const StudentInfo *std_info) { StudentInfo *new_std; new_std=g_new(StudentInfo,1); new_std->number=std_info->number; new_std->name=g_string_new(std_info->name->str); new_std->grade=std_info->grade; return new_std; } void student_info_free(StudentInfo *std_info) { g_string_free(std_info->name,TRUE); g_free(std_info); } G_DEFINE_BOXED_TYPE(StudentInfo,student_info,student_info_copy,student_info_free); #define TYPE_STUDENT_INFO (student_info_get_type()) int main() { StudentInfo *stdi; StudentInfo *stdi_copy; g_type_init(); stdi=student_info_new(1,"JIm",86.5); stdi_copy=g_boxed_copy(TYPE_STUDENT_INFO,stdi); student_info_print(stdi_copy); g_boxed_free(TYPE_STUDENT_INFO,stdi); g_boxed_free(TYPE_STUDENT_INFO,stdi_copy); return 0; }