Gobject tutorial 十一

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值