使用Boxed Type机制包装C语言结构体
注:之前曾写过一篇类似文章,结果被我误删了
按照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; }
2011年1月29日 09:44
还是一个字,累....
不过还是支持一下...
2011年1月29日 12:14
@pingf: 谢谢啦
2011年9月12日 06:37
支持一个,期待更多gobject的文章
2022年8月25日 15:18
Argos doesn't currently offer a student discount, however they offer year round sales both online and in store, argos discount code and you can also find great discount vouchers online. Keep an eye on this website for the latest deals we find so you can get yourself a bargain. student discount card and app giving you access to huge offers on food and essentials, tech, travel and home delivery.Argos doesn't currently offer a student discount, however they offer year round sales both online and in store.