参考:GObject – 2.0
Generic Value Container
GValue结构体是一个变量容器,它包含一个类型成员和类型对应的值。其定义如下:
/*** GValue:* * An opaque structure used to hold different types of values.** The data within the structure has protected scope: it is accessible only* to functions within a #GTypeValueTable structure, or implementations of* the g_value_*() API. That is, code portions which implement new fundamental* types.** #GValue users cannot make any assumptions about how data is stored* within the 2 element @data union, and the @g_type member should* only be accessed through the G_VALUE_TYPE() macro.*/
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];
};
GVaule 变量只能在初始化后使用,初始化函数是g_value_init()。用完之后需要调用g_value_unset()来进行资源释放。
基础的操作(例如free、copy)都有GTypeValueTable中的函数负责,GTypeValueTable是与GValue中的g_type对应。GValue的其他操作(例如,类型转换)都有接口提供。
我们先看看GTypeValueTable的定义。
struct _GTypeValueTable
{GTypeValueInitFunc value_init;GTypeValueFreeFunc value_free;GTypeValueCopyFunc value_copy;GTypeValuePeekPointerFunc value_peek_pointer;const gchar *collect_format;GTypeValueCollectFunc collect_value;const gchar *lcopy_format;GTypeValueLCopyFunc lcopy_value;
};
那,GValue中的g_type与GTypeValueTable是如何对应起来的呢?
我们来看看GLib的基本类型gchar,它是怎么对应二者的。
/* 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);}GType
g_type_register_fundamental (GType type_id,const gchar *type_name,const GTypeInfo *info,const GTypeFundamentalInfo *finfo,GTypeFlags flags)
{......
node = type_node_fundamental_new_W (type_id, type_name, finfo->type_flags);
......if (check_type_info_I (NULL, NODE_FUNDAMENTAL_TYPE (node), type_name, info))type_data_make_W (node, info,check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
......
}static TypeNode*
type_node_fundamental_new_W (GType ftype,const gchar *name,GTypeFundamentalFlags type_flags)
{
......node = type_node_any_new_W (NULL, ftype, name, NULL, type_flags);......return node;
}static TypeNode*
type_node_any_new_W (TypeNode *pnode,GType ftype,const gchar *name,GTypePlugin *plugin,GTypeFundamentalFlags type_flags)
{
......node = g_malloc0 (node_size);if (!pnode) /* offset fundamental types */{
node = G_STRUCT_MEMBER_P (node, SIZEOF_FUNDAMENTAL_INFO);static_fundamental_type_nodes[ftype >> G_TYPE_FUNDAMENTAL_SHIFT] = node;type = ftype;.....}return node;
}
上述代码分析了 g_type_register_fundamental中,ftype与node 的关系,接下来,我们看看node 与GTypeValueTable的关系,这就需要分析g_type_register_fundamental中的另一部分代码。
static void
type_data_make_W (TypeNode *node,const GTypeInfo *info,const GTypeValueTable *value_table)
{
......*vtable = *value_table;......}
通过分析,类型GType与GTypeValueTable是通过数组static_fundamental_type_nodes关联起来的。即,通过TypeNode联系起来的。
static TypeNode *static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };
那么GValue与TypeNode的关系是什么样的呢?
这个问题,需要查看g_value_init函数才能解决。
GValue*
g_value_init (GValue *value,GType g_type)
{
......value_table = g_type_value_table_peek (g_type);
......value_meminit (value, g_type);
...
}static inline void /* keep this function in sync with gvaluecollector.h and gboxed.c */
value_meminit (GValue *value,GType value_type)
{value->g_type = value_type;memset (value->data, 0, sizeof (value->data));
}
通过上述代码,可以知道,在调用g_value_init初始化GVaule变量后,变量的g_type是固定的,以后可以通过g_type,调用函数g_type_value_table_peek,通过数组static_fundamental_type_nodes找到变量类型对应的 GTypeValueTable。
Boxed Types
boxed type是一个通用的、可对任意C结构进行封装的机制。对于boxed type,GObject的类型系统只关心这个结构体如何被复制(对应GBoxedCopyFunc)和如何被释放(对应GBoxedFreeFunc)。
使用boxed type,使得对任意类型的数据结构的处理方式变得统一,可以进行统一方式的复制、释放,对外界的统一表现形式。进而,所有可以进行boxed type处理的数据类型都可以作为GValue结构中的data成员(关键是GValue data数组中有个gpointer),这样,可以对更广范围的数据类型进行多态处理,因此,可以使更多的数据类型可以作为Gobject的属性使用。
所有的boxed type 都继承自G_TYPE_BOXED,这个基础类型。
值得注意的是boxed types 不是深度可继承的,这就是说,你不能对继承自另外一个boxed type的boxted type进行注册。
Registering a new boxed type
我们通过实例来看看怎么注册。
typedef struct _MyBoxed MyBoxed;struct _MyBoxed
{gint ivalue;gchar *bla;
};static gpointer
my_boxed_copy (gpointer orig)
{MyBoxed *a = orig;MyBoxed *b;b = g_slice_new (MyBoxed);b->ivalue = a->ivalue;b->bla = g_strdup (a->bla);return b;
}static gint my_boxed_free_count;static void
my_boxed_free (gpointer orig)
{MyBoxed *a = orig;g_free (a->bla);g_slice_free (MyBoxed, a);my_boxed_free_count++;
}static GType my_boxed_get_type (void);
#define MY_TYPE_BOXED (my_boxed_get_type ())G_DEFINE_BOXED_TYPE (MyBoxed, my_boxed, my_boxed_copy, my_boxed_free)
接下来,我们分析下my_boxed_copy是如何被调用的,首先我们需要先来看看 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, {})#define G_DEFINE_BOXED_TYPE_WITH_CODE(TypeName, type_name, copy_func, free_func, _C_) _G_DEFINE_BOXED_TYPE_BEGIN (TypeName, type_name, copy_func, free_func) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()#define _G_DEFINE_BOXED_TYPE_BEGIN(TypeName, type_name, copy_func, free_func) \
static GType type_name##_get_type_once (void); \
\
GType \
type_name##_get_type (void) \
{ \static gsize static_g_define_type_id = 0; \......GType g_define_type_id = type_name##_get_type_once ();......static GType \
type_name##_get_type_once (void) \
{ \GType (* _g_register_boxed) \(const gchar *, \union \{ \TypeName * (*do_copy_type) (TypeName *); \TypeName * (*do_const_copy_type) (const TypeName *); \GBoxedCopyFunc do_copy_boxed; \} __attribute__((__transparent_union__)), \union \{ \void (* do_free_type) (TypeName *); \GBoxedFreeFunc do_free_boxed; \} __attribute__((__transparent_union__)) \) = g_boxed_type_register_static; \GType g_define_type_id = \_g_register_boxed (g_intern_static_string (#TypeName), copy_func, free_func); \......
}GType
g_boxed_type_register_static (const gchar *name,GBoxedCopyFunc boxed_copy,GBoxedFreeFunc boxed_free)
{static const GTypeValueTable vtable = {boxed_proxy_value_init,boxed_proxy_value_free,boxed_proxy_value_copy,boxed_proxy_value_peek_pointer,"p",boxed_proxy_collect_value,"p",boxed_proxy_lcopy_value,};GTypeInfo type_info = {0, /* class_size */NULL, /* base_init */NULL, /* base_finalize */NULL, /* class_init */NULL, /* class_finalize */NULL, /* class_data */0, /* instance_size */0, /* n_preallocs */NULL, /* instance_init */&vtable, /* value_table */};GType type;g_return_val_if_fail (name != NULL, 0);g_return_val_if_fail (boxed_copy != NULL, 0);g_return_val_if_fail (boxed_free != NULL, 0);g_return_val_if_fail (g_type_from_name (name) == 0, 0);type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);/* install proxy functions upon successful registration */if (type)_g_type_boxed_init (type, boxed_copy, boxed_free);return type;
}void
_g_type_boxed_init (GType type,GBoxedCopyFunc copy_func,GBoxedFreeFunc free_func)
{TypeNode *node = lookup_type_node_I (type);node->data->boxed.copy_func = copy_func;node->data->boxed.free_func = free_func;
}
到此,我们知道my_boxed_copy的存放位置。
Using boxed types
接下来,我们举例说明,在应用程序中,如何使用我们新定义的boxed type的copy函数。
static void
test_define_boxed (void)
{MyBoxed a;MyBoxed *b;a.ivalue = 20;a.bla = g_strdup ("bla");b = g_boxed_copy (MY_TYPE_BOXED, &a);g_assert_cmpint (b->ivalue, ==, 20);g_assert_cmpstr (b->bla, ==, "bla");g_boxed_free (MY_TYPE_BOXED, b);g_free (a.bla);
}
为了搞清楚my_boxed_copy是如何被调用的,我们要分析一下函数g_boxed_copy。
gpointer
g_boxed_copy (GType boxed_type,gconstpointer src_boxed)
{
......value_table = g_type_value_table_peek (boxed_type);
......if (value_table->value_copy == boxed_proxy_value_copy)dest_boxed = _g_type_boxed_copy (boxed_type, (gpointer) src_boxed);else{
......value_table->value_copy (&src_value, &dest_value);}return dest_boxed;
}gpointer
_g_type_boxed_copy (GType type, gpointer value)
{TypeNode *node = lookup_type_node_I (type);return node->data->boxed.copy_func (value);
}
my_boxed_copy中的if判断,代码应该怎么走呢? 根据g_boxed_type_register_static中的对GTypeInfo的成员value_table的设置,很容易知道,value_table->value_copy == boxed_proxy_value_copy,这个条件是成立的。