注:之前曾写过一篇类似文章,结果被我误删了
按照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
#include
#include
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;
}