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,这个条件是成立的。