GObject消息系统:Closure (转)

Closures

Closures是异步信号传递概念的中心,它被广泛的应用到了GTK+和GNOME应用程序中。一个Closure是一个抽象的、通用表示的回调(callback)。它是一个包含三个对象的简单结构:

  • 一个函数指针(回调本身) ,原型类似于:return_type function_callback (... , gpointer user_data);
  • user_data指针用来传递到callback在调用Closure时。
  • 一个函数指针,代表Closure的销毁:当Closure的引用数达到0时,这个函数将被调用来释放Closure的结构。

GClosure结构代表所有Closure实现的共同基础:现在存在着很多各自不同的使用GObject类型系统的运行环境。GObject库提供一个简单的GCClosure类型,来明确使用C/C++的回调来实现。

一个GClosure提供以下简单的服务:

  • 调用(g_closure_invoke):这就是Closure创建的目的: 它们隐藏了回调者的回调细节。
  • 通知:相关事件的Closure通知监听者如Closure调用,Closure无效和Clsoure终结。监听者可以用注册g_closure_add_finalize_notifier(终结通知),g_closure_add_invalidate_notifier(无效通知)和g_closure_add_marshal_guards(调用通知)。对于终结和无效事件来说,这是对等的函数(g_closure_remove_finalize_notifier和g_closure_remove_invalidate_notifier,但调用过程不是。

 

C Closures

如果你用C或C++来通过一个给定的事件来连接一个回调,你可以选择使用一个简单的GCClosures来拥有一个完美又小的API或者使用更简单的g_signal_connect函数(后面将讲这个):

 

GClosure* g_cclosure_new (GCallback        callback_func,

 

gpointer user_data,

GClosureNotify destroy_data);

GClosure* g_cclosure_new_swap (GCallback callback_func,

gpointer user_data,

GClosureNotify destroy_data);

GClosure* g_signal_type_cclosure_new (GType itype,

guint struct_offset);

 

g_cclosure_new将创建一个新的closure将创建一个调用用户提供的回调函数,用户提供的user_data作为一个最后的参数。当closure被终结后(销毁的第二步),它将调用destroy_data函数如果用户提供的话。

g_cclosure_new_swap将创建一个closure来调用用户提供的回调函数使用用户提供的用户数据作为第一个参数(用来取代第用g_cclosure_new创建的最后一个参数)。当closure被终结后(销毁的第二步),它将调用destroy_data函数如果用户提供过的话。

non-C closures (for the fearless)

我们在前面提到过,Closure隐藏了回调请求的细节。在C语言中, 回调请求就像是函数调用:它在堆栈上为被调用的函数创建正确的框架并执行一个呼叫说明。

C Closure 转换目标函数中代表参数的GValue数组为C语言式的参数列表,用这新的参数列表来调用C函数,得到函数的返回值,并将其转换为一个GValue并将其返回给呼叫者。

下面的代码用C实现了一个简单的C函数,用整型作为它的第一个参数,并返回空值。

 

g_cclosure_marshal_VOID__INT (GClosure     *closure,

 

GValue *return_value,

guint n_param_values,

const GValue *param_values,

gpointer invocation_hint,

gpointer marshal_data)

{

typedef void (*GMarshalFunc_VOID__INT) (gpointer data1,

gint arg_1,

gpointer data2);

register GMarshalFunc_VOID__INT callback;

register GCClosure *cc = (GCClosure*) closure;

register gpointer data1, data2;

g_return_if_fail (n_param_values == 2);

data1 = g_value_peek_pointer (param_values + 0);

data2 = closure->data;

callback = (GMarshalFunc_VOID__INT) (marshal_data ? marshal_data : cc->callback);

callback (data1,

g_marshal_value_peek_int (param_values + 1),

data2);

}

 

当然,还存在其他形式的调用。比如,James Henstridge写了一个通用的Python marshaller,用在所有的Python Closure上(一个Python Closure就是closure调用过程中,使其拥有一个基于Python的回调)。这个python marshaller转换输入的代表函数参数的GValue列表到一个Python的等价结构的tuple(你可以在GNOME CVS服务器上查看pygobject结构的源码pygtype.c看到pyg_closure_marshal)

实际应用中,Closure是用来设定语言运行时的边界:如果你正在写Python代码,并且某个Python回调函数收到了一个GTK+控件的信号,所以GTK+的C代码需要执行Python代码。由GTK+对象调用的Closure将调用Python的回调:它的行为就是GTK+的C对象,也是一个标准的Python代码的Python对象。

小结一下:gsignal的new/connect/emit.

Signal New:

1. New 一个SignalNode, 放入到全局数组中:g_signal_nodes

2. 创建 Class Closure:

    1). 生成一个Closure

    2). 指定Class member method 作为default handler

          closure->notifiers[0].data = marshal_data; // default handler, 使用的是class的指针偏移

          closure->notifiers[0].notify = (GClosureNotify) meta_marshal; //一般是g_type_class_meta_marshal 或者g_type_iface_meta_marshal

3. 指定SignalNode的C Marshaller:

    node->c_marshaller = c_marshaller;

4. 把class closure放入SignalNode的成员数组中:

    node->class_closure_bsa = g_bsearch_array_insert (node->class_closure_bsa,   &g_class_closure_bconfig,

5. 指定这个class closure的C_Marshaller:

    closure->marshal = node->c_marshaller

 

Signal connect:

1. New 一个Handler

2. New 一个closure, 并让closure持有用户自己的callback: C Handler [User Handler]

3. 把handler放入全局的handler_list

4. 指定closure的marshaller:

    handler->closure->marshal = node->marshaller;

 

Signal Emit:

1. 根据signal id, 找到signal node*

2. 对输入参数打包为GValue形式

3. 开始处理各种回调:

   1)在class closure中,回调调用顺序:

       meta_marshal --> C Marshaller --> Default Handler [call member method]

   2) 在Handler list中

      c_marsaller --> user callback

4. 返回数据再转回C语言形式

 

更简洁的版本:

Signal New:

注册该信号的default handler + C_marshaller

Signal connect:

注册该信号的user handlers

Signal Emit:

分五个阶段:

阶段 标志 动作
1 G_SIGNAL_RUN_FIRST 调用class的default method handler
2 G_CONNECT_AFTER=FALSE 调用user handlers
3 G_SIGNAL_RUN_LAST 调用class的default method handler
4 G_CONNECT_AFTER=TRUE 调用user handlers
5 G_SIGNAL_RUN_CLEANUP 调用object method handler

 

gobjec相关学习文章的list.

 

从gst的have-type信号,看signal new/connect/emit.

gobjec相关学习文章的list.

 

上层: Caller (Signal connect + user callback )

下层: Callee (Signal New + Emission机制)

 

New signal:

 

gsttypefindelement.c:

  /**
   * GstTypeFindElement::have-type:
   * @typefind: the typefind instance
   * @probability: the probability of the type found
   * @caps: the caps of the type found
   *
   * This signal gets emitted when the type and its probability has
   * been found.
   */
  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have-type",
      G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_FIRST,
      G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
      gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
      G_TYPE_UINT, GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE);

  typefind_class->have_type =
      GST_DEBUG_FUNCPTR (gst_type_find_element_have_type);

这里2个比较重要:

1. 指定类的成员函数:G_STRUCT_OFFSET (GstTypeFindElementClass, have_type),

    是通过偏移地址进行的,在后面的函数g_type_class_meta_marshal里面,会根据这个函数偏移地址offset, 提取出这个函数的指针

2. 指定c_marshal: gst_marshal_VOID__UINT_BOXED

 

跟着代码进去:

guint
g_signal_new (const gchar	 *signal_name,
	      GType		  itype,
	      GSignalFlags	  signal_flags,
	      guint               class_offset,
	      GSignalAccumulator  accumulator,
	      gpointer		  accu_data,
	      GSignalCMarshaller  c_marshaller,
	      GType		  return_type,
	      guint		  n_params,
	      ...)
{
  va_list args;
  guint signal_id;

  g_return_val_if_fail (signal_name != NULL, 0);
  
  va_start (args, n_params);

  signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
                                   class_offset ? g_signal_type_cclosure_new (itype, class_offset) : NULL,
				   accumulator, accu_data, c_marshaller,
                                   return_type, n_params, args);

因为class_offset !=0, 所以会首先调用g_signal_type_cclosure_new (itype, class_offset),

GClosure*
g_signal_type_cclosure_new (GType    itype,
			    guint    struct_offset)
{
  GClosure *closure;
  
  g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
  g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL);
  
  closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
  if (G_TYPE_IS_INTERFACE (itype))
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
  else
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
  
  return closure;
}

g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);

void
g_closure_set_meta_marshal (GClosure       *closure,
			    gpointer        marshal_data,
			    GClosureMarshal meta_marshal)
{
  GClosureNotifyData *notifiers;

  g_return_if_fail (closure != NULL);
  g_return_if_fail (meta_marshal != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (closure->meta_marshal == 0);

  notifiers = closure->notifiers;
  closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1);
  if (notifiers)
    {
      /* usually the meta marshal will be setup right after creation, so the
       * g_memmove() should be rare-case scenario
       */
      g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0]));
      g_free (notifiers);
    }
 

  //marshal_data ->  gst_type_find_element_have_type()  
  closure->notifiers[0].data = marshal_data;
 

  //mata_marshal --> g_type_class_meta_marshal
  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
  SET (closure, meta_marshal, 1);
}

注意,这个meta_marshal 函数暂时还不会被调用。

 

假设被调用,其实就是emit后,发生的动作:

先根据offset, 提取函数的指针:

static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

callback = G_STRUCT_MEMBER (gpointer, class, offset);
到这里,

callback = gst_type_find_element_have_type()= marshal_data ,这个函数指针会作为marshal_data,传入meta_marshal,

meta_marshal = g_type_class_meta_marshal()


static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

 

同时后面会继续进行初始化:

  signal_id = g_signal_newv (signal_name, itype, signal_flags,
			     class_closure, accumulator, accu_data, c_marshaller,
			     return_type, n_params, param_types);
  /* setup permanent portion of signal node */
  if (!node)
    {
      SignalKey key;
      
      signal_id = g_n_signal_nodes++;
      node = g_new (SignalNode, 1);
      node->signal_id = signal_id;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[signal_id] = node;
      node->itype = itype;
      node->name = name;
      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
    }
  node->destroyed = FALSE;
  node->test_class_offset = 0;

  /* setup reinitializable portion */
  node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
  node->n_params = n_params;
  node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
  node->return_type = return_type;
  node->class_closure_bsa = NULL;
  if (accumulator)
    {
      node->accumulator = g_new (SignalAccumulator, 1);
      node->accumulator->func = accumulator;
      node->accumulator->data = accu_data;
    }
  else
    node->accumulator = NULL;
  node->c_marshaller = c_marshaller;
  node->emission_hooks = NULL;
  if (class_closure)
    signal_add_class_closure (node, 0, class_closure);

先把C_marshaller保存下来:node->c_marshaller = c_marshaller;

这里node->c_marshaller: gst_marshal_VOID__UINT_BOXED

 

前面g_signal_type_cclosure_new (itype, class_offset)创建了class的closure, 并且指定了:

1. ) 指向class成员的gst_type_find_element_have_type(),这个估计是default handler.

2. )指定 class closure的meta marshal: g_type_class_meta_marshal()

下面这个函数,又给这个class closure指定了一个C Marshaller:

static void
signal_add_class_closure (SignalNode *node,
			  GType       itype,
			  GClosure   *closure)
{
  ClassClosure key;

  /* can't optimize NOP emissions with overridden class closures */
  node->test_class_offset = 0;

  if (!node->class_closure_bsa)
    node->class_closure_bsa = g_bsearch_array_create (&g_class_closure_bconfig);
  key.instance_type = itype;
  key.closure = g_closure_ref (closure);
  node->class_closure_bsa = g_bsearch_array_insert (node->class_closure_bsa,
						    &g_class_closure_bconfig,
						    &key);
  g_closure_sink (closure);
  if (node->c_marshaller && closure && G_CLOSURE_NEEDS_MARSHAL (closure))
    g_closure_set_marshal (closure, node->c_marshaller);
}

 

在后面调用时:

meta_marshal -->调用 C Marshaller --> 调用 callback类的成员函数(default handler)

此时:这个signal node节点手里握有3个API:

1. class的meta marshal: g_type_class_meta_marshal()

2. C marshaller: gst_marshal_VOID__UINT_BOXED

3. class的成员函数:gst_type_find_element_have_type()【default handler】

 

 

connect signal注册的user级别的callback, 会存放到handler list中;

Connect Signal:

很多地方可以connect那个signal:

比如:gst-typefind.c

  g_signal_connect (G_OBJECT (typefind), "have-type",
      G_CALLBACK (have_type_handler), &caps);
#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

这里注册C_handler, 可以说是上层用户自己的callback.

gulong
g_signal_connect_data (gpointer       instance,
		       const gchar   *detailed_signal,
		       GCallback      c_handler,
		       gpointer       data,
		       GClosureNotify destroy_data,
		       GConnectFlags  connect_flags)
{
  guint signal_id;
  gulong handler_seq_no = 0;
  GQuark detail = 0;
  GType itype;
  gboolean swapped, after;

  swapped = (connect_flags & G_CONNECT_SWAPPED) != FALSE;
  after = (connect_flags & G_CONNECT_AFTER) != FALSE;

  SIGNAL_LOCK ();
  itype = G_TYPE_FROM_INSTANCE (instance);
  signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
  if (signal_id)
    {
      SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);


	{
	  Handler *handler = handler_new (after);

	  handler_seq_no = handler->sequential_number;
	  handler->detail = detail;
	  handler->closure = g_closure_ref ((swapped ? g_cclosure_new_swap : g_cclosure_new) (c_handler, data, destroy_data));
	  g_closure_sink (handler->closure);
	  handler_insert (signal_id, instance, handler);
	  if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (handler->closure))
	    g_closure_set_marshal (handler->closure, node->c_marshaller);
	}
    }
  else
    g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
  SIGNAL_UNLOCK ();

  return handler_seq_no;
}

1. 分配一个hanlder内存

2. 创建一个C closure, 并且绑定user 自己的callback

3. handler只有这个closure, 也就是拿住了用户的callback

4. 把hanlder, 插入到全局的handler list中去

5. 也把signal node的C marshal: gst_marshal_VOID__UINT_BOXED, 和handler的closure绑定。

此时:这个handler的closure掌握了:

1. User defined callback: have_type_handler()   【user defined handler】

2. C Marshal: gst_marshal_VOID__UINT_BOXED

 

从上面的2个步骤可以看出:

Signal的Class Closure 和Signal Handler的Closure的 C Marshaller

指向同一个:C Marshal: gst_marshal_VOID__UINT_BOXED

 

Signal Emit:

在某种条件下,会调用

  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
      probability, caps);

激发条件,暂时先不看。

 

看发射的过程,这里干了什么?如何和new/connect呼应起来?

void
g_signal_emit (gpointer instance,
	       guint    signal_id,
	       GQuark   detail,
	       ...)
{
  va_list var_args;

  va_start (var_args, detail);
  g_signal_emit_valist (instance, signal_id, detail, var_args);
  va_end (var_args);
}
void
g_signal_emit_valist (gpointer instance,
		      guint    signal_id,
		      GQuark   detail,
		      va_list  var_args)
{
  GValue *instance_and_params;
  GType signal_return_type;
  GValue *param_values;
  SignalNode *node;
  guint i, n_params;
  
  g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
  g_return_if_fail (signal_id > 0);

  SIGNAL_LOCK ();
  node = LOOKUP_SIGNAL_NODE (signal_id);
  if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
    {
      g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
      SIGNAL_UNLOCK ();
      return;
    }
#ifndef G_DISABLE_CHECKS
  if (detail && !(node->flags & G_SIGNAL_DETAILED))
    {
      g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
      SIGNAL_UNLOCK ();
      return;
    }
#endif  /* !G_DISABLE_CHECKS */

  /* optimize NOP emissions */
  if (signal_check_skip_emission (node, instance, detail))
    {
      /* nothing to do to emit this signal */
      SIGNAL_UNLOCK ();
      /* g_printerr ("omitting emission of \"%s\"\n", node->name); */
      return;
    }

  n_params = node->n_params;
  signal_return_type = node->return_type;
  instance_and_params = g_slice_alloc0 (sizeof (GValue) * (n_params + 1));
  param_values = instance_and_params + 1;

  for (i = 0; i < node->n_params; i++)
    {
      gchar *error;
      GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
      gboolean static_scope = node->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;

      SIGNAL_UNLOCK ();
      G_VALUE_COLLECT_INIT (param_values + i, ptype,
			    var_args,
			    static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
			    &error);
      if (error)
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);

	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occoured
	   */
	  while (i--)
	    g_value_unset (param_values + i);

	  g_slice_free1 (sizeof (GValue) * (n_params + 1), instance_and_params);
	  return;
	}
      SIGNAL_LOCK ();
    }
  SIGNAL_UNLOCK ();
  instance_and_params->g_type = 0;
  g_value_init (instance_and_params, G_TYPE_FROM_INSTANCE (instance));
  g_value_set_instance (instance_and_params, instance);
  if (signal_return_type == G_TYPE_NONE)
    signal_emit_unlocked_R (node, detail, instance, NULL, instance_and_params);
  else
    {
      GValue return_value = { 0, };
      gchar *error = NULL;
      GType rtype = signal_return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
      gboolean static_scope = signal_return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
      
      g_value_init (&return_value, rtype);

      signal_emit_unlocked_R (node, detail, instance, &return_value, instance_and_params);

      G_VALUE_LCOPY (&return_value,
		     var_args,
		     static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
		     &error);
      if (!error)
	g_value_unset (&return_value);
      else
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);
	  
	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occured
	   */
	}
    }
  for (i = 0; i < n_params; i++)
    g_value_unset (param_values + i);
  g_value_unset (instance_and_params);
  g_slice_free1 (sizeof (GValue) * (n_params + 1), instance_and_params);
}

1. 把输入的C参数list放到一个GValue, Collect 中

2. 调用signal_emit_unblocked_R(), 并且GValue参数作为最后一个参数

3. 把返回的值再弄回C语言的形式。

 

下面重点看看:signal_emit_unblocked_R(),

在这个函数里面,会做如下的动作:

1. 找到class closure ( callback [default handler] + c_marshaller)

2. 找到别人注册到该信号的handlers (User callback + c_marshaller)

之后就是根据条件进行回调的处理:

有3个重要的条件:

1. if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)  //这个处理class自己注册的handler, 一般是指定好的成员函数

    if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)

2. if (node->emission_hooks)

3. if (handler_list)   //这个处理用g_signal_connect_xxx()注册的用户自己的callback (user defined handlers)

 

先看第一个:

  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);
      if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
	  emission.state == EMISSION_RUN)
	emission.state = EMISSION_STOP;
      SIGNAL_LOCK ();
      emission.chain_type = G_TYPE_NONE;
      return_value_altered = TRUE;
      
      if (emission.state == EMISSION_STOP)
	goto EMIT_CLEANUP;
      else if (emission.state == EMISSION_RESTART)
	goto EMIT_RESTART;
    }
void
g_closure_invoke (GClosure       *closure,
		  GValue /*out*/ *return_value,
		  guint           n_param_values,
		  const GValue   *param_values,
		  gpointer        invocation_hint)
{
  g_return_if_fail (closure != NULL);

  g_closure_ref (closure);      /* preserve floating flag */
  if (!closure->is_invalid)
    {
      GClosureMarshal marshal;
      gpointer marshal_data;
      gboolean in_marshal = closure->in_marshal;

      g_return_if_fail (closure->marshal || closure->meta_marshal);

      SET (closure, in_marshal, TRUE);
      if (closure->meta_marshal)
	{
	  marshal_data = closure->notifiers[0].data;
	  marshal = (GClosureMarshal) closure->notifiers[0].notify;
	}
      else
	{
	  marshal_data = NULL;
	  marshal = closure->marshal;
	}
      if (!in_marshal)
	closure_invoke_notifiers (closure, PRE_NOTIFY);
      marshal (closure,
	       return_value,
	       n_param_values, param_values,
	       invocation_hint,
	       marshal_data);
      if (!in_marshal)
	closure_invoke_notifiers (closure, POST_NOTIFY);
      SET (closure, in_marshal, in_marshal);
    }
  g_closure_unref (closure);
}

前面在New Signal的时候,就已经创建了class closure, 并且注册了:

marshal_data = gst_type_find_element_have_type()

mashal = g_type_class_meta_marshal()//这个mashal是我们指定的meta mashaller

所以会执行下面的:

      marshal (closure,
	       return_value,
	       n_param_values, param_values,
	       invocation_hint,
	       marshal_data);

即:

static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

这个函数非常有意思:

1. 根据传入的marshal_data ( gst_type_find_element_have_typed的地址),取出其offset

2. 根据offset, 取出对应class中的成员函数:callback = G_STRUCT_MEMBER (gpointer, class, offset);

    提取的结果:callback = gst_type_find_element_have_typed()

3. 在把这个callback作为最后一个参数传入到closure的C marshal: gst_marshal_VOID__UINT_BOXED

 真正执行的是下面的函数:

gst_marshal_VOID_UINT_BOXED(closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      gst_type_find_element_have_typed);

 

在gstmarshal.c中:

/* VOID:UINT,BOXED (./gstmarshal.list:19) */
void
gst_marshal_VOID__UINT_BOXED (GClosure     *closure,
                              GValue       *return_value G_GNUC_UNUSED,
                              guint         n_param_values,
                              const GValue *param_values,
                              gpointer      invocation_hint G_GNUC_UNUSED,
                              gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__UINT_BOXED) (gpointer     data1,
                                                 guint        arg_1,
                                                 gpointer     arg_2,
                                                 gpointer     data2);
  register GMarshalFunc_VOID__UINT_BOXED callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 3);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__UINT_BOXED) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_uint (param_values + 1),
            g_marshal_value_peek_boxed (param_values + 2),
            data2);
}

    {
      data1 = g_value_peek_pointer (param_values + 0);  //拿到输入参数list
      data2 = closure->data; // = (gpointer) itype, 在GClosure*g_closure_new_simple (guint           sizeof_closure,      gpointer        data)指定的
    }
  callback = (GMarshalFunc_VOID__UINT_BOXED) (marshal_data ? marshal_data : cc->callback);

  marshal_data != NULL, 所以这里的callback使用的是marshal_data: gst_type_find_element_have_typed

 

  gst_type_find_element_have_typed (参数GValue,
            g_marshal_value_peek_uint (param_values + 1),
            g_marshal_value_peek_boxed (param_values + 2),
            itype);

 

在gsttypefindelement.c

static void
gst_type_find_element_have_type (GstTypeFindElement * typefind,
    guint probability, const GstCaps * caps)
{
  GstCaps *copy;

  g_assert (caps != NULL);

  GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", probability=%u",
      caps, probability);

  GST_OBJECT_LOCK (typefind);
  if (typefind->caps)
    gst_caps_unref (typefind->caps);
  typefind->caps = gst_caps_copy (caps);
  copy = gst_caps_ref (typefind->caps);
  GST_OBJECT_UNLOCK (typefind);

  gst_pad_set_caps (typefind->src, copy);
  gst_caps_unref (copy);
}

 

      G_VALUE_LCOPY (&return_value,             var_args,             static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,             &error);

这个是把GValue的值再转回C语言的形式。

 

gobjec相关学习文章的list.

gsignal 1.new/2.connect/3.emit

gobjec相关学习文章的list.

 

我们从一个实际的例子看看:

New Signal:

1. New一个signal

在类初始化时,先指定了成员method:

klass->show = gtk_widget_real_show;

 然后定义信号:

  widget_signals[SHOW] =
    g_signal_new (I_("show"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkWidgetClass, show),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);

这里注意:G_STRUCT_OFFSET( GtkWidgetClass, show)

这个是靠地址偏移,找到GtkWidgetClass的成员函数指针的地址

 

2. 往下看:

guint
g_signal_new (const gchar	 *signal_name,
	      GType		  itype,
	      GSignalFlags	  signal_flags,
	      guint               class_offset,
	      GSignalAccumulator  accumulator,
	      gpointer		  accu_data,
	      GSignalCMarshaller  c_marshaller,
	      GType		  return_type,
	      guint		  n_params,
	      ...)
{
  va_list args;
  guint signal_id;

  g_return_val_if_fail (signal_name != NULL, 0);
  
  va_start (args, n_params);

  signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
                                   class_offset ? g_signal_type_cclosure_new (itype, class_offset) : NULL,
				   accumulator, accu_data, c_marshaller,
                                   return_type, n_params, args);

  va_end (args);

一般情况下,class_offset不是0, 所以会调用:g_signal_type_cclosure_new (itype, class_offset)

GClosure*
g_signal_type_cclosure_new (GType    itype,
			    guint    struct_offset)
{
  GClosure *closure;
  
  g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
  g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL);
  
  closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
  if (G_TYPE_IS_INTERFACE (itype))
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
  else
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
  
  return closure;
}

GUINT_TO_POINTER (struct_offset), 这个就是函数指针:指的就是 GtkWidgetClass的Method:(*show)

这个(*show)作为回调函数的。

往下看:

void
g_closure_set_meta_marshal (GClosure       *closure,
			    gpointer        marshal_data,
			    GClosureMarshal meta_marshal)
{
  GClosureNotifyData *notifiers;

  g_return_if_fail (closure != NULL);
  g_return_if_fail (meta_marshal != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (closure->meta_marshal == 0);

  notifiers = closure->notifiers;
  closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1);
  if (notifiers)
    {
      /* usually the meta marshal will be setup right after creation, so the
       * g_memmove() should be rare-case scenario
       */
      g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0]));
      g_free (notifiers);
    }
  closure->notifiers[0].data = marshal_data;
  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
  SET (closure, meta_marshal, 1);
}

这里的marshal_data, 就是上面的:GUINT_TO_POINTER (struct_offset), 就是用户指定的callback了。

同时,这个callback作为最后一个参数,传入:g_type_iface_meta_marshal

static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

在这个函数里面,首先提取callback.

如果有callback的化,后面会把callback做为最后一个参数,传入到marshal里面;

比如:_gtk_marshal_VOID__VOID == g_cclosure_marshal_VOID__VOID

void
g_cclosure_marshal_VOID__VOID (GClosure     *closure,
                               GValue       *return_value G_GNUC_UNUSED,
                               guint         n_param_values,
                               const GValue *param_values,
                               gpointer      invocation_hint G_GNUC_UNUSED,
                               gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__VOID) (gpointer     data1,
                                           gpointer     data2);
  register GMarshalFunc_VOID__VOID callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 1);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__VOID) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            data2);
}

最后真正调用的这是前面指定的callback: klass->show = gtk_widget_real_show;

 

上边把user callback同closure绑定之后,g_signal_new_valist会调用g_signal_newv

guint
g_signal_newv (const gchar       *signal_name,
               GType              itype,
               GSignalFlags       signal_flags,
               GClosure          *class_closure,
               GSignalAccumulator accumulator,
	       gpointer		  accu_data,
               GSignalCMarshaller c_marshaller,
               GType		  return_type,
               guint              n_params,
               GType		 *param_types)
{
  gchar *name;
  guint signal_id, i;
  SignalNode *node;
  
  g_return_val_if_fail (signal_name != NULL, 0);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
  if (n_params)
    g_return_val_if_fail (param_types != NULL, 0);
  g_return_val_if_fail ((return_type & G_SIGNAL_TYPE_STATIC_SCOPE) == 0, 0);
  if (return_type == (G_TYPE_NONE & ~G_SIGNAL_TYPE_STATIC_SCOPE))
    g_return_val_if_fail (accumulator == NULL, 0);
  if (!accumulator)
    g_return_val_if_fail (accu_data == NULL, 0);

  name = g_strdup (signal_name);
  g_strdelimit (name, G_STR_DELIMITERS ":^", '_');  /* FIXME do character checks like for types */
  
  SIGNAL_LOCK ();
  
  signal_id = signal_id_lookup (g_quark_try_string (name), itype);
  node = LOOKUP_SIGNAL_NODE (signal_id);

  /* setup permanent portion of signal node */
  if (!node)
    {
      SignalKey key;
      
      signal_id = g_n_signal_nodes++;
      node = g_new (SignalNode, 1);
      node->signal_id = signal_id;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[signal_id] = node;
      node->itype = itype;
      node->name = name;
      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
    }
  node->destroyed = FALSE;
  node->test_class_offset = 0;

  /* setup reinitializable portion */
  node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
  node->n_params = n_params;
  node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
  node->return_type = return_type;
  node->class_closure_bsa = NULL;
  if (accumulator)
    {
      node->accumulator = g_new (SignalAccumulator, 1);
      node->accumulator->func = accumulator;
      node->accumulator->data = accu_data;
    }
  else
    node->accumulator = NULL;
  node->c_marshaller = c_marshaller;
  node->emission_hooks = NULL;
  if (class_closure)
    signal_add_class_closure (node, 0, class_closure);
  else if (G_TYPE_IS_INSTANTIATABLE (itype) && return_type == G_TYPE_NONE)
    {
      /* optimize NOP emissions */
      node->test_class_offset = TEST_CLASS_MAGIC;
    }
  SIGNAL_UNLOCK ();

  g_free (name);

  return signal_id;
}

上面这个函数完成:

1. 如果是新建的Signal, 则把它放入SignalNode数组中:g_signal_nodes[signal_id]

2. 并且放入用于查询的二分数组中:g_signal_key_bsa

3. 因为user指定了自己的回调,所以signal_add_class_closure()把class closure也放入

    到相关的二分查找数组中:node->class_closure_bsa

4. 指定c_marshal, 这是:_gtk_marshal_VOID__VOID

 

New signal的小结:

1. Signal Node生成

2. callback指定

3. c Marshal指定

 

Signal Connect:

gsignal.c提供了3个相关的API:

gulong	 g_signal_connect_closure_by_id	      (gpointer		  instance,
					       guint		  signal_id,
					       GQuark		  detail,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_closure	      (gpointer		  instance,
					       const gchar       *detailed_signal,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_data		      (gpointer		  instance,
					       const gchar	 *detailed_signal,
					       GCallback	  c_handler,
					       gpointer		  data,
					       GClosureNotify	  destroy_data,
					       GConnectFlags	  connect_flags);

用的最多的是g_signal_connect_data, 直接用它的也有,不过大多是用经过包装后的:

gtype.h:

#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)
#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED)

以及:

gobject.c:

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)

这个函数其实是封装:g_signal_connect_closure/g_signal_connect_data

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)
{
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
  g_return_val_if_fail (detailed_signal != NULL, 0);
  g_return_val_if_fail (c_handler != NULL, 0);

  if (gobject)
    {
      GClosure *closure;

      g_return_val_if_fail (G_IS_OBJECT (gobject), 0);

      closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject);

      return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
    }
  else
    return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags);
}

 

 

 Signal Emit: 

void
gtk_widget_show (GtkWidget *widget)
{
  g_return_if_fail (GTK_IS_WIDGET (widget));

  if (!gtk_widget_get_visible (widget))
    {
      g_object_ref (widget);
      gtk_widget_push_verify_invariants (widget);

      if (!gtk_widget_is_toplevel (widget))
        gtk_widget_queue_resize (widget);

      /* see comment in set_parent() for why this should and can be
       * conditional
       */
      if (widget->priv->need_compute_expand ||
          widget->priv->computed_hexpand ||
          widget->priv->computed_vexpand)
        {
          if (widget->priv->parent != NULL)
            gtk_widget_queue_compute_expand (widget->priv->parent);
        }

      g_signal_emit (widget, widget_signals[SHOW], 0);
      g_object_notify (G_OBJECT (widget), "visible");

      gtk_widget_pop_verify_invariants (widget);
      g_object_unref (widget);
    }
}

 

void
g_signal_emit (gpointer instance,
	       guint    signal_id,
	       GQuark   detail,
	       ...)
{
  va_list var_args;

  va_start (var_args, detail);
  g_signal_emit_valist (instance, signal_id, detail, var_args);
  va_end (var_args);
}

 

static gboolean
signal_emit_unlocked_R (SignalNode   *node,
			GQuark	      detail,
			gpointer      instance,
			GValue	     *emission_return,
			const GValue *instance_and_params)
{
  SignalAccumulator *accumulator;
  Emission emission;
  GClosure *class_closure;
  HandlerList *hlist;
  Handler *handler_list = NULL;
  GValue *return_accu, accu = { 0, };
  guint signal_id;
  gulong max_sequential_handler_number;
  gboolean return_value_altered = FALSE;

  emission.instance = instance;
  emission.ihint.signal_id = node->signal_id;
  emission.ihint.detail = detail;
  emission.ihint.run_type = 0;
  emission.state = 0;
  emission.chain_type = G_TYPE_NONE;
  emission_push ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions, &emission);
  class_closure = signal_lookup_closure (node, instance);
  
 EMIT_RESTART:
  
  if (handler_list)
    handler_unref_R (signal_id, instance, handler_list);
  max_sequential_handler_number = g_handler_sequential_number;
  hlist = handler_list_lookup (signal_id, instance);
  handler_list = hlist ? hlist->handlers : NULL;
  if (handler_list)
    handler_ref (handler_list);
  
  emission.ihint.run_type = G_SIGNAL_RUN_FIRST;
  
  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);
      if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
	  emission.state == EMISSION_RUN)
	emission.state = EMISSION_STOP;
      SIGNAL_LOCK ();
      emission.chain_type = G_TYPE_NONE;
      return_value_altered = TRUE;
      
      if (emission.state == EMISSION_STOP)
	goto EMIT_CLEANUP;
      else if (emission.state == EMISSION_RESTART)
	goto EMIT_RESTART;
    }
  

上面做如下的动作:

1)做一个emmission, 收集好signal的相关信息,并且压入list中,临时保存信息

2)找到和这个signal相关联的closure (callback)

3)  根据signal id, 找到hander list

4)  如果有closure, 则调用g_closure_invoke(), 启动callback流程

  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);

 

   4.1) 调用closure_invoke_notifier() //保护参数

   4.2) 执行marshal(), 其中在marshal中调用 user callback

          因为参数传入时,用GValue形式传入,在marshal中:

          (1)需要把参数由Gvalue --> C 参数形式

          (2)调用user callback

          (3)把返回的值再包装成GValue形式带回

5. 如果通过g_signal_connect_xxx() 注册了自己的回调,会调用handler_list

  if (handler_list)
    {
      Handler *handler = handler_list;
     
      emission.state = EMISSION_RUN;
      handler_ref (handler);
      do
 {
   Handler *tmp;
  
   if (handler->after)
     {
       handler_unref_R (signal_id, instance, handler_list);
       handler_list = handler;
       break;
     }
   else if (!handler->block_count && (!handler->detail || handler->detail == detail) &&
     handler->sequential_number < max_sequential_handler_number)
     {
       SIGNAL_UNLOCK ();
       g_closure_invoke (handler->closure,
    return_accu,
    node->n_params + 1,
    instance_and_params,
    &emission.ihint);

 

gobjec相关学习文章的list.

 

 

 

gobject的调试。

gobjec相关学习文章的list.

gobject中,基本的gtype, 以及继承,派生关系;

gobject 类初始化,对象初始化,接口初始化,方法调用,信号发射等;需要仔细的观察;

所以我们需要函数完成2个功能:

1. 函数调用层级关系显示(类初始化,对象初始化,接口初始化...)

2. gtype/gobject父子关系,显示

 

参考gobject-query.c, 修改一下:

gtype.h:

void g_type_get_node_tree(void);


void g_type_enter_func(const char *name);
void g_type_leave_func(const char *name);

#define G_ENTER_FUNC g_type_enter_func(__FUNCTION__)
#define G_LEAVE_FUNC g_type_leave_func(__FUNCTION__)

gtype.c:

static GType static_gtype_root = G_TYPE_OBJECT;

static void
_g_type_show_nodes (GType        type,
	    GType        sibling,
	    const gchar *indent)
{
	static gchar *indent_inc = NULL;
	static guint spacing = 1;
	static gboolean recursion = TRUE;

#define	O_SPACE	" "
#define	O_ESPACE ""
#define	O_BRANCH "+"
#define	O_VLINE "|"
#define	O_LLEAF	"`"
#define	O_KEY_FILL "_"

	GType   *children;
	guint i;

	if (!type)
		return;

	if (!indent_inc)
	{
		indent_inc = g_new (gchar, strlen (O_SPACE) + 1);
		*indent_inc = 0;
		strcpy (indent_inc, O_SPACE);
	}  
  
	children = g_type_children (type, NULL);

	if (type != static_gtype_root)
		for (i = 0; i < spacing; i++)
			g_printf("%s%s\n", indent, O_VLINE);
  
	g_printf("%s%s%s%s",
				indent,
				sibling ? O_BRANCH : (type != static_gtype_root ? O_LLEAF : O_SPACE),
				O_ESPACE,
				g_type_name (type));

	for (i = strlen (g_type_name (type)); i <= strlen (indent_inc); i++)
		g_printf("%s", O_KEY_FILL);
	g_printf("\n");

	if (children && recursion)
	{
		gchar *new_indent;
		GType   *child;

		if (sibling)
			new_indent = g_strconcat (indent, O_VLINE, indent_inc, NULL);
		else
			new_indent = g_strconcat (indent, O_SPACE, indent_inc, NULL);

		for (child = children; *child; child++)
			_g_type_show_nodes (child[0], child[1], new_indent);

		g_free (new_indent);
	}

	g_free (children);
}

void g_type_get_node_tree(void)
{
	gint i;
	
  	static_gtype_root = ~0;
	for (i = 0; i <= G_TYPE_FUNDAMENTAL_MAX; i += G_TYPE_MAKE_FUNDAMENTAL (1))
	{
		const gchar *name = g_type_name (i);
		if (name)
			_g_type_show_nodes (i, 0, "");
	}

}


static guint g_static_depth = 0;

void g_type_enter_func(const char *name)
{
	guint i;
	const char *s;
	if (g_static_depth == 1)	
		g_printf(" \n");
	for (i = 0; i < g_static_depth; i++)
		g_printf("+");
	g_printf("Entering %s\n", name);
	g_static_depth++;
}

void g_type_leave_func(const char *name)
{
	const char *s;
	guint i;
	if (g_static_depth != 0) 	
        	g_static_depth--;
	if (g_static_depth < 0)  
		g_static_depth = 0;

	for (i = 0; i < g_static_depth; i++) 
		g_printf("-");
	g_printf("Leaving  %s\n", name);
}

 

修改gobject/testgobject.c

可以看到的结果:

Entering main

+Entering test_object_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_get_type

+Entering iface_base_init
-Leaving  iface_base_init

+Entering iface_base_init
-Leaving  iface_base_init

+Entering test_object_class_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_class_init

+Entering test_object_test_iface_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_iface_init

+Entering derived_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  derived_object_get_type

+Entering iface_base_init
-Leaving  iface_base_init

+Entering derived_object_class_init
-Leaving  derived_object_class_init

+Entering derived_object_test_iface_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  derived_object_test_iface_init

+Entering test_object_init
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  test_object_init

+Entering derived_object_init
++Entering derived_object_get_type
--Leaving  derived_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  derived_object_init

+Entering test_object_check_private_init
++Entering test_object_get_type
--Leaving  test_object_get_type
private data during initialization: 54321 == 54321
-Leaving  test_object_check_private_init
|
`void
|
`GInterface
  |
  +GTypePlugin
  |
  `TestIface
|
`gchar
|
`guchar
|
`gboolean
|
`gint
|
`guint
|
`glong
|
`gulong
|
`gint64
|
`guint64
|
`GEnum
|
`GFlags
|
`gfloat
|
`gdouble
|
`gchararray
|
`gpointer
  |
  `GType
|
`GBoxed
  |
  `GValueArray
|
`GParam
  |
  +GParamChar
  |
  +GParamUChar
  |
  +GParamBoolean
  |
  +GParamInt
  |
  +GParamUInt
  |
  +GParamLong
  |
  +GParamULong
  |
  +GParamInt64
  |
  +GParamUInt64
  |
  +GParamUnichar
  |
  +GParamEnum
  |
  +GParamFlags
  |
  +GParamFloat
  |
  +GParamDouble
  |
  +GParamString
  |
  +GParamParam
  |
  +GParamBoxed
  |
  +GParamPointer
  |
  +GParamValueArray
  |
  +GParamObject
  |
  +GParamOverride
  |
  +GParamGType
  |
  `GParamVariant
|
`GObject
  |
  `TestObject
    |
    `DerivedObject
|
`GVariant
|
`FooShadow1
|
`FooShadow2

+Entering test_object_get_type
-Leaving  test_object_get_type

+Entering test_object_init
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  test_object_init
MAIN: emit test-signal:
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal

+Entering test_signal_accumulator
-Leaving  test_signal_accumulator
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal

+Entering test_signal_accumulator
-Leaving  test_signal_accumulator
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal
TestObject-Message: signal return: "<default_handler><default_handler>"
MAIN: call iface print-string on test and derived object:

+Entering iface_print_string
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering print_foo
Iface-FOO: "iface-string-from-test-type" from 0x9370a00
--Leaving  print_foo
-Leaving  iface_print_string

+Entering iface_print_string
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering print_bar
+++Entering test_iface_get_type
---Leaving  test_iface_get_type
Iface-BAR: "iface-string-from-derived-type" from 0x936aaf0
chaining: +++Entering test_iface_get_type
---Leaving  test_iface_get_type
+++Entering print_foo
Iface-FOO: "iface-string-from-derived-type" from 0x936aaf0
---Leaving  print_foo
--Leaving  print_bar
-Leaving  iface_print_string

+Entering test_object_get_type
-Leaving  test_object_get_type
private data after initialization: 54321 == 54321

Leaving  main

gobjec相关学习文章的list.

 

 

 

GTYPE类型系统分析 zz

转载时请注明出处和作者联系方式

作者联系方式:会飞的鱼 <parker30_liu at hotmail dot com>

刚开始接触GLib库时,对GLib传统的习惯和特有的概念不熟悉,在编写基于GLib的程序时总是很不顺手,心中很是不爽。后来随着写的代码多了,渐渐熟悉了GLib的这些术语、概念、编码风格,情况好多了。但对GLib的类型系统还是不大清楚,只不过是照葫芦画瓢学的不错而已,对基于GType的使用在较深入时还是一知半解。最近有了些空余时间,于是去仔细看了GType相关的代码,总算弄得比较清楚了。记录在此,以便查阅,有不对之处,请高手指正。

Gtype提供了GLib的数据类型系统和数据类型定义,是GObject对象系统的基础。GLib定义了一些基本数据类型,并允许用户定义一些基本类型的派生类型,还可以定义动态类型,如GObject的对象系统的各种类型的对象都是GObject的派生类型。GType只支持单继承,但允许通过接口继承的方式实现变形的多继承,避免了类似C++的多继承的复杂性和多继承导致的各种问题,规范了程序员对多继承的使用。GType提供了一个基于C语言的具有完备的多层抽象结构的继承体系的良好实现,使得我们可以使用C语言来进行面向对象的程序设计和实现。

首先,从GType这个数据类型的定义开始,一步一步地进入这个复杂的GType类型系统,让我们来解开这个迷宫吧。下面的分析是基于GLib的2.12.6版本的源代码来进行的,可能和较早的版本有些出入,请大家注意。

在最新的GLib中,GType是一个ulong类型,即无符号长整型数。GType的意义是用来指向整个GLib类型系统的一个类型节点。对于基本类型,GType是该类型在基本类型中的序号乘以4(乘以4是用来和非基本类型兼容,内存地址低二位为0)。对于非基本类型,GType是该类型的类型节点的类型信息的内存地址。

基本类型最多有256个,现在GLib只使用了49个,包括GLib已经定义的基本类型、GLib保留的基本类型、BSE保留的基本类型,其余的基本类型给用户使用。下面是GLib中已经定义的基本类型。

 

 

#define G_TYPE_INVALID G_TYPE_MAKE_FUNDAMENTAL (0)

#define G_TYPE_NONE G_TYPE_MAKE_FUNDAMENTAL (1)

#define G_TYPE_INTERFACE G_TYPE_MAKE_FUNDAMENTAL (2)

#define G_TYPE_CHAR G_TYPE_MAKE_FUNDAMENTAL (3)

#define G_TYPE_UCHAR G_TYPE_MAKE_FUNDAMENTAL (4)

#define G_TYPE_BOOLEAN G_TYPE_MAKE_FUNDAMENTAL (5)

#define G_TYPE_INT G_TYPE_MAKE_FUNDAMENTAL (6)

#define G_TYPE_UINT G_TYPE_MAKE_FUNDAMENTAL (7)

#define G_TYPE_LONG G_TYPE_MAKE_FUNDAMENTAL (8)

#define G_TYPE_ULONG G_TYPE_MAKE_FUNDAMENTAL (9)

#define G_TYPE_INT64 G_TYPE_MAKE_FUNDAMENTAL (10)

#define G_TYPE_UINT64 G_TYPE_MAKE_FUNDAMENTAL (11)

#define G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12)

#define G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13)

#define G_TYPE_FLOAT G_TYPE_MAKE_FUNDAMENTAL (14)

#define G_TYPE_DOUBLE G_TYPE_MAKE_FUNDAMENTAL (15)

#define G_TYPE_STRING G_TYPE_MAKE_FUNDAMENTAL (16)

#define G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17)

#define G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18)

#define G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19)

#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)

 

宏G_TYPE_MAKE_FUNDAMENTAL是将类型序号乘以4以得到基本类型的值。除了基本数据使用的类型如G_TYPE_CHAR等外,还有如下几个基本类型。

  • G_TYPE_INVALID

无效的GType类型,此种类型不是GType类型。

  • G_TYPE_NONE

空的GType类型,和C语言的void相当。

  • G_TYPE_BOXED

多个GType类型的数据组合在一起的得到的GType类型,可以是单一GType类型数据组成的数组,也可以是不同GType类型数据组成的异质数组。如字符串数组(G_TYPE_STRV)、GValue数组(G_TYPE_VALUE_ARRAY)、日期数据类型(G_TYPE_DATE)。此类型只能单层继承。

  • G_TYPE_PARAM

参数数据类型,用于进行函数、信号等的参数传递的GType类型。

  • G_TYPE_OBJECT

GObject对象的GType类型,是GLib对象系统的根对象类型,是一个可以实例化的类类型,可以多层继承,但只能单根继承。G_TYPE_OBJECT类型的派生类型的实例化对象在不混淆的情况下也可以称为GObject对象。

  • G_TYPE_INTERFACE

接口类型,定义了对数据或对象进行操作的接口方法,凡实现了某一个接口的所有方法的GObject对象可以称其继承了该接口。

Gtype的基本类型是可以继承的,其继承关系由类型节点确定,类型节点保存了GType类型的类型信息。所有基本类型的类型节点的指针保存在一个基本类型节点数组中,基本类型的值除以4就是该基本类型节点在基本类型节点数组中的序号。

类型节点的数据结构如下所示。

 

struct _TypeNode

{

GTypePlugin *plugin;

guint n_children : 12;

guint n_supers : 8;

guint _prot_n_ifaces_prerequisites : 9;

guint is_classed : 1;

guint is_instantiatable : 1;

guint mutatable_check_cache : 1; /* combines some common path checks */

GType *children;

TypeData * volatile data;

GQuark qname;

GData *global_gdata;

union {

IFaceEntry *iface_entries; /* for !iface types */

GType *prerequisistes;

} _prot;

GType supers[1]; /* flexible array */

};

类型节点数据结构中的supers和children确定了各个GType类型的继承关系。n_supers是所继承的所有祖先类型的个数,数组supers保存了该类型继承的所有祖先类型,n_children是所有子类型的个数,数组children保存了该类型的所有子类型。所有GType类型组成了下图所示的关系。


 

图中箭头由派生类指向基类,虚线框的节点类型是和我们的分析无关的继承关系。该图描述了无继承,单层继承,多层继承这几种继承关系。

基本类型的类型节点除了普通类型的信息外,还有基本类型的类型信息。基本类型的信息决定了该基本类型及其派生类型的如下几个特性。

  • G_TYPE_FLAG_CLASSED

该基本类型是一个类类型。

  • G_TYPE_FLAG_INSTANTIABLE

该基本类型是可以实例化的类型,即是否可以创建该类型的对象。

  • G_TYPE_DERIVABLE

该基本类型是可以单层继承的类型,只允许一层的平坦的继承。

  • G_TYPE_DEEP_DERIVABLE

该基本类型是可以多层继承的类型,可以有子类型、孙子类型,子子孙孙无穷尽也。

普通类型的信息有如下几个类型特性。

  • G_TYPE_FLAG_ABSTRACT

该类型是一个抽象类型。

  • G_TYPE_FLAG_VALUE_ABSTRACT

该类型是一个值抽象类型,是GValue用的,表明该类型的实例是一个GValue值

 

每个类型节点的祖先类型数组supers是按如下方式排列,祖先类型数组的大小是1(self) + n_supers + 1(0),n_supers最大值是255,即最大继承层次是255层。

 

自身类型

父类型

祖父类型

...

基本类型

0

 

每个类型节点的子类型按创建的次序增加到子类型数组children中,n_children最大值是4095,即最多有4095个子类型。

类型节点数据结构的qname保存了该GType类型的名字,这是一个夸克(夸克是GLib的特有术语,相当于字符串的ID)。plugin是用于动态类型的类型数据入口。

is_classed指明该类型是否是一个类,is_instantiatable指明该类型是否可以实例化。这两个成员的值从基本类型继承而来。

global_gdata是GType的一个特色,用于保存GType类型相关的附属信息,其本身是一个由QData组成的动态数组GData。每个QData是个夸克指针对,夸克保存该信息的名字,指针指向该信息的内容。可以通过函数g_type_set_qdata()设置该类型相关的附属信息,通过函数g_type_get_qdata()获取该类型相关的附属信息。这是一个十分方便和好用的功能。

 

_prot是一个联合,当该类型是一个接口时,是一个由GType类型组成的动态数组,实现这个接口的GType必须要实现该GType类型数组中的所有GType类型,该数组中一般都是接口类型,最多只有一个可实例化的类型。若不是接口,是一般的类,则是一个由接口入口IFaceEntry组成的动态数组,这个数组保存了这个类继承的所有接口。_prot_n_ifaces_prerequisites是动态数组元素的个数。

data是该GType类型的类型数据,保存了和该类型相关的接口、类或实例对象的数据。类型数据的数据结构如下所示。

 

union _TypeData

{

CommonData common;

IFaceData iface;

ClassData class;

InstanceData instance;

};

 

 

 

这是一个联合,当类型是一个接口时,为接口数据IFaceData。当类型是一个类时,为类数据ClassData。当类型是一个对象实例时,为对象数据InstanceData。当类型是其他一般的类型时,为类型公共数据CommonData。接口数据IFaceData、类数据ClassData、对象数据InstanceData都包含有公共数据CommonData。

 

 

CommonData的数据结构如下所示。

 

struct _CommonData

{

guint ref_count;

GTypeValueTable *value_table;

};

ref_count是引用计数,是大部分面向对象系统用来保证对象能够在复杂的环境下正确的创建、引用、销毁的一种技术。

value_table是该gtype类型的值处理函数表,如值的初始化函数、值的释放函数、值拷贝函数等,这是同类型的gtype实例之间实现值拷贝的基础。下面是GTypeValueTable的数据结构。

 

struct _GTypeValueTable

{

void (*value_init) (GValue *value);

void (*value_free) (GValue *value);

void (*value_copy) (const GValue *src_value,

GValue *dest_value);

/* varargs functionality (optional) */

gpointer (*value_peek_pointer) (const GValue *value);

gchar *collect_format;

gchar* (*collect_value) (GValue *value,

guint n_collect_values,

GTypeCValue *collect_values,

guint collect_flags);

gchar *lcopy_format;

gchar* (*lcopy_value) (const GValue *value,

guint n_collect_values,

GTypeCValue *collect_values,

guint collect_flags);

};

 

gobject运行时重要的数据结构。

 

 数据定义

 说明

gtype

static TypeNode                               *static_fundamental_type_nodes[256]

一个全局指针数组, 用来保存基本类型;

通过基本类型顺藤摸瓜,可以找到其子类型

static GHashTable       *static_type_nodes_ht = NULL;

一个全局的Hash Table

这个Hash Table一般是通过“名字”来查询TypeNode时使用

 

 

 

Signal

static SignalNode   **g_signal_nodes = NULL;

双指针 == 指针数组?

  static GBSearchArray *g_signal_key_bsa = NULL;  方便根据signal name/type来查询signal_id

 

static GHashTable    *g_handler_list_bsa_ht = NULL;

信号处理函数用hash table

 

static Emission      *g_recursive_emissions = NULL;

 

 

static Emission      *g_restart_emissions = NULL;

 

 

object

static GQuark                 quark_closure_array = 0;

 这个和CArray关系紧密;是Object的回调函数数组的标志。

 

static GQuark                 quark_weak_refs = 0;

 

 

static GQuark                 quark_toggle_refs = 0;

 

 

static GObjectNotifyContext property_notify_context = { 0, };

 

 

static gulong                   gobject_signals[LAST_SIGNAL] = { 0, };

全局数组

 

param_spec

static GParamSpecPool *pspec_pool = NULL;

 用Hash Table来保存Object的ParamSpec

 

/* --- type initialization --- */

GType *g_param_spec_types = NULL;

一个全局数组

Gtype.c中

1. 定义:

//这个用处比较少,只是用“名字”来查询时用
static GHashTable       *static_type_nodes_ht = NULL;

//这个用的比较多,访问数组查询效率高
static TypeNode		*static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };

2. 初始化

void
g_type_init_with_debug_flags (GTypeDebugFlags debug_flags)
{
  G_LOCK_DEFINE_STATIC (type_init_lock);
  const gchar *env_string;
  GTypeInfo info;
  TypeNode *node;
  volatile GType votype;

  
   
  /* type qname hash table */
  static_type_nodes_ht = g_hash_table_new (g_direct_hash, g_direct_equal);
  
  /* invalid type G_TYPE_INVALID (0)
   */
  static_fundamental_type_nodes[0] = NULL;

3. New 一个Type:

type_node_any_new_W()

如果是基本类型的,直接放入数组中:static_fundamental_type_nodes;

如果有父类,则查到父类,然后放入父类的子类数组pnode->children[i]中;

同时把新建的Type, 放入Hash Table:static_type_nodes_ht中。

static TypeNode*
type_node_any_new_W (TypeNode             *pnode,
		     GType                 ftype,
		     const gchar          *name,
		     GTypePlugin          *plugin,
		     GTypeFundamentalFlags type_flags)
{
  guint n_supers;
  GType type;
  TypeNode *node;
  guint i, node_size = 0;

  n_supers = pnode ? pnode->n_supers + 1 : 0;
  
  if (!pnode)
    node_size += SIZEOF_FUNDAMENTAL_INFO;	      /* fundamental type info */
  node_size += SIZEOF_BASE_TYPE_NODE ();	      /* TypeNode structure */
  node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + (0) for ->supers[] */
  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;
    }
  else
    type = (GType) node;
  
  g_assert ((type & TYPE_ID_MASK) == 0);
  
  node->n_supers = n_supers;
  if (!pnode)
    {
      node->supers[0] = type;
      node->supers[1] = 0;
      
      node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0;
      node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0;
      
      if (NODE_IS_IFACE (node))
	{
          IFACE_NODE_N_PREREQUISITES (node) = 0;
	  IFACE_NODE_PREREQUISITES (node) = NULL;
	}
      else
	_g_atomic_array_init (CLASSED_NODE_IFACES_ENTRIES (node));
    }
  else
    {
      node->supers[0] = type;
      memcpy (node->supers + 1, pnode->supers, sizeof (GType) * (1 + pnode->n_supers + 1));
      
      node->is_classed = pnode->is_classed;
      node->is_instantiatable = pnode->is_instantiatable;
      
      if (NODE_IS_IFACE (node))
	{
	  IFACE_NODE_N_PREREQUISITES (node) = 0;
	  IFACE_NODE_PREREQUISITES (node) = NULL;
	}
      else
	{
	  guint j;
	  IFaceEntries *entries;

	  entries = _g_atomic_array_copy (CLASSED_NODE_IFACES_ENTRIES (pnode),
					  IFACE_ENTRIES_HEADER_SIZE,
					  0);
	  if (entries)
	    {
	      for (j = 0; j < IFACE_ENTRIES_N_ENTRIES (entries); j++)
		{
		  entries->entry[j].vtable = NULL;
		  entries->entry[j].init_state = UNINITIALIZED;
		}
	      _g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node),
				      entries);
	    }
	}

      i = pnode->n_children++;
      pnode->children = g_renew (GType, pnode->children, pnode->n_children);
      pnode->children[i] = type;
    }

  TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type));

  
  g_hash_table_insert (static_type_nodes_ht,
		       GUINT_TO_POINTER (node->qname),
		       (gpointer) type);
  return node;
}

4. 查询TypeNode,

用的比较多的是lookup_type_node_I,直接访问数组

static inline TypeNode*
lookup_type_node_I (register GType utype)
{
  if (utype > G_TYPE_FUNDAMENTAL_MAX)
    return (TypeNode*) (utype & ~TYPE_ID_MASK);
  else
    return static_fundamental_type_nodes[utype >> G_TYPE_FUNDAMENTAL_SHIFT];
}

 

当使用Type Name来查询时,访问Hash Table比较快:

这个函数目前看,使用还不是很多。

GType
g_type_from_name (const gchar *name)
{
  GType type = 0;
  GQuark quark;
  
  g_return_val_if_fail (name != NULL, 0);
  
  quark = g_quark_try_string (name);
  if (quark)
    {
      G_READ_LOCK (&type_rw_lock);
      type = (GType) g_hash_table_lookup (static_type_nodes_ht, GUINT_TO_POINTER (quark));
      G_READ_UNLOCK (&type_rw_lock);
    }
  
  return type;
}

 

GParamSpec.c:

1. 定义一个全局指针g_param_spec_types,引用时也可以用数组的方式进行:

/* --- type initialization --- */
GType *g_param_spec_types = NULL;

 

2. 初始化

 首先分配一块内存给g_param_spec_types:

然后注册23个基本的参数类型,同时对每种参数类型,都提供相应的处理函数:

void
g_param_spec_types_init (void)	
{
  const guint n_types = 23;
  GType type, *spec_types, *spec_types_bound;

  g_param_spec_types = g_new0 (GType, n_types);
  spec_types = g_param_spec_types;
  spec_types_bound = g_param_spec_types + n_types;
  
  /* G_TYPE_PARAM_CHAR
   */
  {
    static const GParamSpecTypeInfo pspec_info = {
      sizeof (GParamSpecChar),	/* instance_size */
      16,			/* n_preallocs */
      param_char_init,		/* instance_init */
      G_TYPE_CHAR,		/* value_type */
      NULL,			/* finalize */
      param_char_set_default,	/* value_set_default */
      param_char_validate,	/* value_validate */
      param_int_values_cmp,	/* values_cmp */
    };
    type = g_param_type_register_static (g_intern_static_string ("GParamChar"), &pspec_info);
    *spec_types++ = type;
    g_assert (type == G_TYPE_PARAM_CHAR);
  }
  

 基本23个参数类型:

  +GParamChar
  |
  +GParamUChar
  |
  +GParamBoolean
  |
  +GParamInt
  |
  +GParamUInt
  |
  +GParamLong
  |
  +GParamULong
  |
  +GParamInt64
  |
  +GParamUInt64
  |
  +GParamUnichar
  |
  +GParamEnum
  |
  +GParamFlags
  |
  +GParamFloat
  |
  +GParamDouble
  |
  +GParamString
  |
  +GParamParam
  |
  +GParamBoxed
  |
  +GParamPointer
  |
  +GParamValueArray
  |
  +GParamObject
  |
  +GParamOverride
  |
  +GParamGType
  |
  `GParamVariant

3. 引用这个数组:

初始化后,就可以直接使用这个数组了:

今后就用宏直接访问该参数类型,以及其关联的处理函数了。

#define	G_TYPE_PARAM_CHAR		   (g_param_spec_types[0])
#define	G_TYPE_PARAM_UCHAR		   (g_param_spec_types[1])
#define	G_TYPE_PARAM_BOOLEAN		   (g_param_spec_types[2])
#define	G_TYPE_PARAM_INT		   (g_param_spec_types[3])
#define	G_TYPE_PARAM_UINT		   (g_param_spec_types[4])
#define	G_TYPE_PARAM_LONG		   (g_param_spec_types[5])
#define	G_TYPE_PARAM_ULONG		   (g_param_spec_types[6])
#define	G_TYPE_PARAM_INT64		   (g_param_spec_types[7])
#define	G_TYPE_PARAM_UINT64		   (g_param_spec_types[8])
#define	G_TYPE_PARAM_UNICHAR		   (g_param_spec_types[9])
#define	G_TYPE_PARAM_ENUM		   (g_param_spec_types[10])
#define	G_TYPE_PARAM_FLAGS		   (g_param_spec_types[11])
#define	G_TYPE_PARAM_FLOAT		   (g_param_spec_types[12])
#define	G_TYPE_PARAM_DOUBLE		   (g_param_spec_types[13])
#define	G_TYPE_PARAM_STRING		   (g_param_spec_types[14])
#define	G_TYPE_PARAM_PARAM		   (g_param_spec_types[15])
#define	G_TYPE_PARAM_BOXED		   (g_param_spec_types[16])
#define	G_TYPE_PARAM_POINTER		   (g_param_spec_types[17])
#define	G_TYPE_PARAM_VALUE_ARRAY	   (g_param_spec_types[18])
#define	G_TYPE_PARAM_OBJECT		   (g_param_spec_types[19])
#define	G_TYPE_PARAM_OVERRIDE		   (g_param_spec_types[20])
#define	G_TYPE_PARAM_GTYPE		   (g_param_spec_types[21])
#define G_TYPE_PARAM_VARIANT                (g_param_spec_types[22])

例如,创建参数类型实例:

GParamSpec*
g_param_spec_char (const gchar *name,
		   const gchar *nick,
		   const gchar *blurb,
		   gint8	minimum,
		   gint8	maximum,
		   gint8	default_value,
		   GParamFlags	flags)
{
  GParamSpecChar *cspec;

  g_return_val_if_fail (default_value >= minimum && default_value <= maximum, NULL);

  cspec = g_param_spec_internal (G_TYPE_PARAM_CHAR,
				 name,
				 nick,
				 blurb,
				 flags);
  
  cspec->minimum = minimum;
  cspec->maximum = maximum;
  cspec->default_value = default_value;
  
  return G_PARAM_SPEC (cspec);
}

Gobject install property时,会大量使用上面这类函数:

static void
gst_play_base_bin_class_init (GstPlayBaseBinClass * klass)
{
  GObjectClass *gobject_klass;
  GstElementClass *gstelement_klass;
  GstBinClass *gstbin_klass;

  gobject_klass = (GObjectClass *) klass;
  gstelement_klass = (GstElementClass *) klass;
  gstbin_klass = (GstBinClass *) klass;

  parent_class = g_type_class_peek_parent (klass);

  gobject_klass->set_property = gst_play_base_bin_set_property;
  gobject_klass->get_property = gst_play_base_bin_get_property;

  g_object_class_install_property (gobject_klass, ARG_URI,
      g_param_spec_string ("uri", "URI", "URI of the media to play",
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_SUBURI,
      g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE,
      g_param_spec_uint64 ("queue-size", "Queue size",
          "Size of internal queues in nanoseconds", 0, G_MAXINT64,
          DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_QUEUE_THRESHOLD,
      g_param_spec_uint64 ("queue-threshold", "Queue threshold",
          "Buffering threshold of internal queues in nanoseconds", 0,
          G_MAXINT64, DEFAULT_QUEUE_THRESHOLD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_QUEUE_MIN_THRESHOLD,
      g_param_spec_uint64 ("queue-min-threshold", "Queue min threshold",
          "Buffering low threshold of internal queues in nanoseconds", 0,
          G_MAXINT64, DEFAULT_QUEUE_MIN_THRESHOLD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_klass, ARG_NSTREAMS,
      g_param_spec_int ("nstreams", "NStreams", "number of streams",
          0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_STREAMINFO,
      g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo",
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_STREAMINFO_VALUES,
      g_param_spec_value_array ("stream-info-value-array",
          "StreamInfo GValueArray", "value array of streaminfo",
          g_param_spec_object ("streaminfo", "StreamInfo", "Streaminfo object",
              GST_TYPE_STREAM_INFO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS),
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_SOURCE,
      g_param_spec_object ("source", "Source", "Source element",
          GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

  g_object_class_install_property (gobject_klass, ARG_VIDEO,
      g_param_spec_int ("current-video", "Current video",
          "Currently playing video stream (-1 = none)",
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_AUDIO,
      g_param_spec_int ("current-audio", "Current audio",
          "Currently playing audio stream (-1 = none)",
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_TEXT,
      g_param_spec_int ("current-text", "Current text",
          "Currently playing text stream (-1 = none)",
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, ARG_SUBTITLE_ENCODING,
      g_param_spec_string ("subtitle-encoding", "subtitle encoding",
          "Encoding to assume if input subtitles are not in UTF-8 encoding. "
          "If not set, the GST_SUBTITLE_ENCODING environment variable will "
          "be checked for an encoding to use. If that is not set either, "
          "ISO-8859-15 will be assumed.", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  /**
   * GstPlayBaseBin:connection-speed
   *
   * Network connection speed in kbps (0 = unknown)
   * <note><simpara>
   * Since version 0.10.10 in #GstPlayBin, at 0.10.15 moved to #GstPlayBaseBin
   * </simpara></note>
   *
   * Since: 0.10.10
   */
  g_object_class_install_property (gobject_klass, ARG_CONNECTION_SPEED,
      g_param_spec_uint ("connection-speed", "Connection Speed",
          "Network connection speed in kbps (0 = unknown)",
          0, G_MAXUINT, DEFAULT_CONNECTION_SPEED,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

  GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0,
      "playbasebin");

  gobject_klass->dispose = gst_play_base_bin_dispose;
  gobject_klass->finalize = gst_play_base_bin_finalize;

  gstbin_klass->handle_message =
      GST_DEBUG_FUNCPTR (gst_play_base_bin_handle_message_func);

  gstelement_klass->change_state =
      GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state);
}

注意:g_param_spec_types数组仅仅保存上面预定义的23个基本类型的参数,以及其对应的处理函数。

通过g_param_spec_xxx() 实例化出来的参数实例,是保持在另外一个参数池GParamSpecPool中的。参加下面内容:

 

GObject的属性如何保持的?

在Gobject.c中,

1. 定义一个静态全局变量:

static GParamSpecPool      *pspec_pool = NULL;

属性池其实就是:互斥保护+散列表

2. 申请一块内存:

static void
g_object_do_class_init (GObjectClass *class)
{
  /* read the comment about typedef struct CArray; on why not to change this quark */
  quark_closure_array = g_quark_from_static_string ("GObject-closure-array");

  quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
  quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
  pspec_pool = g_param_spec_pool_new (TRUE);
  property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
  property_notify_context.dispatcher = g_object_notify_dispatcher;

3. 把参数属性,插入到属性池中,也就是放到Hash Table中:

static inline void
install_property_internal (GType       g_type,
			   guint       property_id,
			   GParamSpec *pspec)
{
  if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
    {
      g_warning ("When installing property: type `%s' already has a property named `%s'",
		 g_type_name (g_type),
		 pspec->name);
      return;
    }

  g_param_spec_ref (pspec);
  g_param_spec_sink (pspec);
  PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
  g_param_spec_pool_insert (pspec_pool, pspec, g_type);
}
void
g_param_spec_pool_insert (GParamSpecPool *pool,
			  GParamSpec     *pspec,
			  GType           owner_type)
{
  gchar *p;
  
  if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
    {
      G_SLOCK (&pool->smutex);
      for (p = pspec->name; *p; p++)
	{
	  if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
	    {
	      g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
	      G_SUNLOCK (&pool->smutex);
	      return;
	    }
	}
      
      pspec->owner_type = owner_type;
      g_param_spec_ref (pspec);
      g_hash_table_insert (pool->hash_table, pspec, pspec);

4. 从属性池中查询属性:

static GParamSpecPool      *pspec_pool = NULL;
list = g_param_spec_pool_list_owned (pspec_pool, G_OBJECT_CLASS_TYPE (class));
g_param_spec_pool_remove (pspec_pool, pspec);
pspec_pool = g_param_spec_pool_new (TRUE);
if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
g_param_spec_pool_insert (pspec_pool, pspec, g_type);
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE);
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, parent_type, TRUE);
pspec = g_param_spec_pool_lookup (pspec_pool,
return g_param_spec_pool_lookup (pspec_pool,
overridden = g_param_spec_pool_lookup (pspec_pool,
overridden = g_param_spec_pool_lookup (pspec_pool,
pspecs = g_param_spec_pool_list (pspec_pool,
pspecs = g_param_spec_pool_list (pspec_pool,
pspec = g_param_spec_pool_lookup (pspec_pool,
pspecs = g_param_spec_pool_list (pspec_pool, iface_type, &n);
GParamSpec *class_pspec = g_param_spec_pool_lookup (pspec_pool,
GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool,
GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool,
pspec = g_param_spec_pool_lookup (pspec_pool,
pspec = g_param_spec_pool_lookup (pspec_pool,
pspec = g_param_spec_pool_lookup (pspec_pool,
pspec = g_param_spec_pool_lookup (pspec_pool,

 

5. 从Hash Table中提取所有的属性,到链表中,

用g_param_spec_pool_list

 

6. 从属性池中,根据属性名字,提取一个属性:

g_param_spec_pool_lookup

 

Gobject的属性如何install上去的:

安装属性,其实就是把属性放到hash table中:

void
g_object_class_install_property (GObjectClass *class,
				 guint	       property_id,
				 GParamSpec   *pspec)
void
g_object_class_install_properties (GObjectClass  *oclass,
                                   guint          n_pspecs,
                                   GParamSpec   **pspecs)
void
g_object_interface_install_property (gpointer      g_iface,
				     GParamSpec   *pspec)
{
  GTypeInterface *iface_class = g_iface;
	
  g_return_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type));
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
  g_return_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (pspec)); /* paranoid */
  g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);	/* paranoid */
		    
  install_property_internal (iface_class->g_type, 0, pspec);
}

最后都是放到那个全局变量pspec_pool中:

static inline void
install_property_internal (GType       g_type,
			   guint       property_id,
			   GParamSpec *pspec)
{
  if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
    {
      g_warning ("When installing property: type `%s' already has a property named `%s'",
		 g_type_name (g_type),
		 pspec->name);
      return;
    }

  g_param_spec_ref (pspec);
  g_param_spec_sink (pspec);
  PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
  g_param_spec_pool_insert (pspec_pool, pspec, g_type);
}

 

GObject的属性被被改变时,如何响应的:

 

 

GObject的回调数组

GObject的回调,实际上是为了处理用户定义的signal/callback.

1. 定义个数据结构:

typedef struct {
  GObject  *object;
  guint     n_closures;
  GClosure *closures[1]; /* flexible array */
} CArray;  //closure Array

同时定义一个标识:

static GQuark	            quark_closure_array = 0;
static void
g_object_do_class_init (GObjectClass *class)
{
  /* read the comment about typedef struct CArray; on why not to change this quark */
  quark_closure_array = g_quark_from_static_string ("GObject-closure-array");

2. 从object的回调数组 中拿掉一个回调:

static void
object_remove_closure (gpointer  data,
		       GClosure *closure)
{
  GObject *object = data;
  CArray *carray;
  guint i;
  
  G_LOCK (closure_array_mutex);
  carray = g_object_get_qdata (object, quark_closure_array);
  for (i = 0; i < carray->n_closures; i++)
    if (carray->closures[i] == closure)
      {
	carray->n_closures--;
	if (i < carray->n_closures)
	  carray->closures[i] = carray->closures[carray->n_closures];
	G_UNLOCK (closure_array_mutex);
	return;
      }
  G_UNLOCK (closure_array_mutex);
  g_assert_not_reached ();
}

3. 给Object增加一个需要监视的回调(GClosure), 放到回调数组中(Closure Array)

void
g_object_watch_closure (GObject  *object,
			GClosure *closure)
{
  CArray *carray;
  guint i;
  
  g_return_if_fail (G_IS_OBJECT (object));
  g_return_if_fail (closure != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (object->ref_count > 0);	/* this doesn't work on finalizing objects */
  
  g_closure_add_invalidate_notifier (closure, object, object_remove_closure);
  g_closure_add_marshal_guards (closure,
				object, (GClosureNotify) g_object_ref,
				object, (GClosureNotify) g_object_unref);
  G_LOCK (closure_array_mutex);
  carray = g_datalist_id_remove_no_notify (&object->qdata, quark_closure_array);
  if (!carray)
    {
      carray = g_renew (CArray, NULL, 1);
      carray->object = object;
      carray->n_closures = 1;
      i = 0;
    }
  else
    {
      i = carray->n_closures++;
      carray = g_realloc (carray, sizeof (*carray) + sizeof (carray->closures[0]) * i);
    }
  carray->closures[i] = closure;
  g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array);
  G_UNLOCK (closure_array_mutex);
}

4. 创建一个新的回调:注册user callback, 并且绑定Object, 同时加入Object的 Callback Array中:

GClosure*
g_cclosure_new_object (GCallback callback_func,
		       GObject  *object)
{
  GClosure *closure;

  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
  g_return_val_if_fail (object->ref_count > 0, NULL);     /* this doesn't work on finalizing objects */
  g_return_val_if_fail (callback_func != NULL, NULL);

  closure = g_cclosure_new (callback_func, object, NULL);
  g_object_watch_closure (object, closure);

  return closure;
}

5. 把signal和信号回调联系在一起

这时候,把signal和object的callback array联系在一起了:

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)
{
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
  g_return_val_if_fail (detailed_signal != NULL, 0);
  g_return_val_if_fail (c_handler != NULL, 0);

  if (gobject)
    {
      GClosure *closure;

      g_return_val_if_fail (G_IS_OBJECT (gobject), 0);

      closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject);

      return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
    }
  else
    return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags);
}

 

6. 进一步的封装:可以链接一串信号

gpointer
g_object_connect (gpointer     _object,
		  const gchar *signal_spec,
		  ...)
{
  GObject *object = _object;
  va_list var_args;

  g_return_val_if_fail (G_IS_OBJECT (object), NULL);
  g_return_val_if_fail (object->ref_count > 0, object);

  va_start (var_args, signal_spec);
  while (signal_spec)
    {
      GCallback callback = va_arg (var_args, GCallback);
      gpointer data = va_arg (var_args, gpointer);
      gulong sid;

      if (strncmp (signal_spec, "signal::", 8) == 0)
	sid = g_signal_connect_data (object, signal_spec + 8,
				     callback, data, NULL,
				     0);
      else if (strncmp (signal_spec, "object_signal::", 15) == 0 ||
               strncmp (signal_spec, "object-signal::", 15) == 0)
	sid = g_signal_connect_object (object, signal_spec + 15,
				       callback, data,
				       0);
      else if (strncmp (signal_spec, "swapped_signal::", 16) == 0 ||
               strncmp (signal_spec, "swapped-signal::", 16) == 0)
	sid = g_signal_connect_data (object, signal_spec + 16,
				     callback, data, NULL,
				     G_CONNECT_SWAPPED);
      else if (strncmp (signal_spec, "swapped_object_signal::", 23) == 0 ||
               strncmp (signal_spec, "swapped-object-signal::", 23) == 0)
	sid = g_signal_connect_object (object, signal_spec + 23,
				       callback, data,
				       G_CONNECT_SWAPPED);
      else if (strncmp (signal_spec, "signal_after::", 14) == 0 ||
               strncmp (signal_spec, "signal-after::", 14) == 0)
	sid = g_signal_connect_data (object, signal_spec + 14,
				     callback, data, NULL,
				     G_CONNECT_AFTER);
      else if (strncmp (signal_spec, "object_signal_after::", 21) == 0 ||
               strncmp (signal_spec, "object-signal-after::", 21) == 0)
	sid = g_signal_connect_object (object, signal_spec + 21,
				       callback, data,
				       G_CONNECT_AFTER);
      else if (strncmp (signal_spec, "swapped_signal_after::", 22) == 0 ||
               strncmp (signal_spec, "swapped-signal-after::", 22) == 0)
	sid = g_signal_connect_data (object, signal_spec + 22,
				     callback, data, NULL,
				     G_CONNECT_SWAPPED | G_CONNECT_AFTER);
      else if (strncmp (signal_spec, "swapped_object_signal_after::", 29) == 0 ||
               strncmp (signal_spec, "swapped-object-signal-after::", 29) == 0)
	sid = g_signal_connect_object (object, signal_spec + 29,
				       callback, data,
				       G_CONNECT_SWAPPED | G_CONNECT_AFTER);
      else
	{
	  g_warning ("%s: invalid signal spec \"%s\"", G_STRFUNC, signal_spec);
	  break;
	}
      signal_spec = va_arg (var_args, gchar*);
    }
  va_end (var_args);

  return object;
}

 

7. 例子:比如gstbin:

  /* use the sync signal handler to link elements while the pipeline is still
   * doing the state change */
  gst_bus_set_sync_handler (bus, gst_bus_sync_signal_handler, pipeline);
  g_object_connect (bus, "signal::sync-message::state-changed",
      G_CALLBACK (test_link_structure_change_state_changed_sync_cb), pipeline,
      NULL);

 

8. 再如:gtk test中:

      button = g_object_new (gtk_button_get_type (),
			       "label", "push something",
			       "visible", TRUE,
			       "parent", box2,
			       NULL);
      g_object_connect (button,
			"signal::clicked", statusbar_push, statusbar,
			NULL);

      button = g_object_connect (g_object_new (gtk_button_get_type (),
						 "label", "pop",
						 "visible", TRUE,
						 "parent", box2,
						 NULL),
				 "signal_after::clicked", statusbar_pop, statusbar,
				 NULL);

      button = g_object_connect (g_object_new (gtk_button_get_type (),
						 "label", "steal #4",
						 "visible", TRUE,
						 "parent", box2,
						 NULL),
				 "signal_after::clicked", statusbar_steal, statusbar,
				 NULL);

      button = g_object_connect (g_object_new (gtk_button_get_type (),
						 "label", "test contexts",
						 "visible", TRUE,
						 "parent", box2,
						 NULL),
				 "swapped_signal_after::clicked", statusbar_contexts, statusbar,
				 NULL);

      button = g_object_connect (g_object_new (gtk_button_get_type (),
						 "label", "push something long",
						 "visible", TRUE,
						 "parent", box2,
						 NULL),
				 "signal_after::clicked", statusbar_push_long, statusbar,
				 NULL);

 

GSignal的SignalNode用指针数组保存:

1. 定义一个双指针

双指针,类似于指针数组;由于数组大小未知,定义为双指针。

/* --- signal nodes --- */
static guint          g_n_signal_nodes = 0;
static SignalNode   **g_signal_nodes = NULL;

如果知道了g_signal_nodes的大小,

可以像TypeNode那样定义:

static TypeNode		*static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };

即:*static_fundamental_type_nodes[256]

 

假设信号最多有128个,我们可以这样定义:

static SignalNode   *g_signal_nodes[128 + 1] = { NULL, }

 

2. 初始化

void
g_signal_init (void)
{
  SIGNAL_LOCK ();
  if (!g_n_signal_nodes)
    {
      /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */
      g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL);
      g_signal_key_bsa = g_bsearch_array_create (&g_signal_key_bconfig);

      
      /* invalid (0) signal_id */
      g_n_signal_nodes = 1;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[0] = NULL;
    }
  SIGNAL_UNLOCK ();
}

3. 创建信号时:

吧新创建的SignalNode放入指针数组g_signal_nodes[signal_id]中:

guint
g_signal_newv (const gchar       *signal_name,
               GType              itype,
               GSignalFlags       signal_flags,
               GClosure          *class_closure,
               GSignalAccumulator accumulator,
	       gpointer		  accu_data,
               GSignalCMarshaller c_marshaller,
               GType		  return_type,
               guint              n_params,
               GType		 *param_types)
{
  gchar *name;
  guint signal_id, i;
  SignalNode *node;

  /* setup permanent portion of signal node */
  ////////////////////////////
  //....................
 
  ////////////////////////////

  if (!node)
    {
      SignalKey key;
      
      signal_id = g_n_signal_nodes++;
      node = g_new (SignalNode, 1);
      node->signal_id = signal_id;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[signal_id] = node;
      node->itype = itype;
      node->name = name;
      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
    }
  node->destroyed = FALSE;
  node->test_class_offset = 0;

4. 查询SignalNode时,直接访问指针数组:

static inline SignalNode*
LOOKUP_SIGNAL_NODE (register guint signal_id)
{
  if (signal_id < g_n_signal_nodes)
    return g_signal_nodes[signal_id];
  else
    return NULL;
}

 

Signal的二分查找数组:

上面定义的数组g_signal_nodes[signal_id]; 在知道signal_id已知的情况下,可以直接访问数组,提取SignalNode*,

但是,很多情况下,我们不知道signal_id, 而知道signal_name.

如果通过signal_name如何查询与之对应的signal id等信息呢?

 

1 在前面New Signal时,我们就看到:为了方便查询,留了后手:

      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

把SignalNode的3个信息:

Node type, node name(-->quark), node signal id, 三位一体的几个简单信息,保存在SignalKey结构变量中,

然后放到一个数组中g_signal_key_bsa;

GBSearchArray是glib提供的一个二分查找数组,可以更快的使用“name"等查到与之对应的signal id.

 

常用的函数:

2 根据quark/type来查找:

/* --- functions --- */
static inline guint
signal_id_lookup (GQuark quark,
		  GType  itype)
{
  GType *ifaces, type = itype;
  SignalKey key;
  guint n_ifaces;

  key.quark = quark;

  /* try looking up signals for this type and its ancestors */
  do
    {
      SignalKey *signal_key;
      
      key.itype = type;
      signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      
      if (signal_key)
	return signal_key->signal_id;
      
      type = g_type_parent (type);
    }
  while (type);

  /* no luck, try interfaces it exports */
  ifaces = g_type_interfaces (itype, &n_ifaces);
  while (n_ifaces--)
    {
      SignalKey *signal_key;

      key.itype = ifaces[n_ifaces];
      signal_key = g_bsearch_array_lookup (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      if (signal_key)
	{
	  g_free (ifaces);
	  return signal_key->signal_id;
	}
    }
  g_free (ifaces);
  
  return 0;
}

3 再封装一次,提供给外界使用:

guint
g_signal_lookup (const gchar *name,
                 GType        itype)
{
  guint signal_id;
  g_return_val_if_fail (name != NULL, 0);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
  
  SIGNAL_LOCK ();
  signal_id = signal_id_lookup (g_quark_try_string (name), itype);
  SIGNAL_UNLOCK ();
  if (!signal_id)
    {
      /* give elaborate warnings */
      if (!g_type_name (itype))
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" for invalid type id `%"G_GSIZE_FORMAT"'",
		   name, itype);
      else if (!G_TYPE_IS_INSTANTIATABLE (itype))
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" for non instantiatable type `%s'",
		   name, g_type_name (itype));
      else if (!g_type_class_peek (itype))
	g_warning (G_STRLOC ": unable to lookup signal \"%s\" of unloaded type `%s'",
		   name, g_type_name (itype));
    }
  
  return signal_id;
}

 

4 第三方库使用:

如gst-inspect:

static void
print_signal_info (GstElement * element)
{
  /* Signals/Actions Block */
  guint *signals;
  guint nsignals;
  gint i = 0, j, k;
  GSignalQuery *query = NULL;
  GType type;
  GSList *found_signals, *l;

  for (k = 0; k < 2; k++) {
    found_signals = NULL;

    /* For elements that have sometimes pads, also list a few useful GstElement
     * signals. Put these first, so element-specific ones come later. */
    if (k == 0 && has_sometimes_template (element)) {
      query = g_new0 (GSignalQuery, 1);
      g_signal_query (g_signal_lookup ("pad-added", GST_TYPE_ELEMENT), query);
      found_signals = g_slist_append (found_signals, query);
      query = g_new0 (GSignalQuery, 1);
      g_signal_query (g_signal_lookup ("pad-removed", GST_TYPE_ELEMENT), query);
      found_signals = g_slist_append (found_signals, query);
      query = g_new0 (GSignalQuery, 1);
      g_signal_query (g_signal_lookup ("no-more-pads", GST_TYPE_ELEMENT),
          query);
      found_signals = g_slist_append (found_signals, query);
    }

 

handler的处理:

这里的处理有点意思,也不知道到底是否高效:

1). handler -->放入handler list, 这没有问题

2). hander list作为key 放入handler_list_bsa (二分查找数组),可以很快查询?

3). handler_list_bsa 有装入了Hash Table中:handler_list_bsa_ht

 

1. 看看New hander_list_bsa_ht

在g_signal_init()时,先创建一个hash tabe:

void
g_signal_init (void)
{
  SIGNAL_LOCK ();
  if (!g_n_signal_nodes)
    {
      /* setup handler list binary searchable array hash table (in german, that'd be one word ;) */
      g_handler_list_bsa_ht = g_hash_table_new (g_direct_hash, NULL);

这个hash table何时用?如何用?

 

2. New 一个Handler

static inline Handler*
handler_new (gboolean after)
{
  Handler *handler = g_slice_new (Handler);
#ifndef G_DISABLE_CHECKS
  if (g_handler_sequential_number < 1)
    g_error (G_STRLOC ": handler id overflow, %s", REPORT_BUG);
#endif
  
  handler->sequential_number = g_handler_sequential_number++;
  handler->prev = NULL;
  handler->next = NULL;
  handler->detail = 0;
  handler->ref_count = 1;
  handler->block_count = 0;
  handler->after = after != FALSE;
  handler->closure = NULL;
  
  return handler;
}

 

3. New 的Handler一般会放到Handler list中 (各种插入)

static void
handler_insert (guint    signal_id,
		gpointer instance,
		Handler  *handler)
{
  HandlerList *hlist;
  
  g_assert (handler->prev == NULL && handler->next == NULL); /* paranoid */
  
  hlist = handler_list_ensure (signal_id, instance);
  if (!hlist->handlers)
    {
      hlist->handlers = handler;
      if (!handler->after)
        hlist->tail_before = handler;
    }
  else if (handler->after)
    {
      handler->prev = hlist->tail_after;
      hlist->tail_after->next = handler;
    }
  else
    {
      if (hlist->tail_before)
        {
          handler->next = hlist->tail_before->next;
          if (handler->next)
            handler->next->prev = handler;
          handler->prev = hlist->tail_before;
          hlist->tail_before->next = handler;
        }
      else /* insert !after handler into a list of only after handlers */
        {
          handler->next = hlist->handlers;
          if (handler->next)
            handler->next->prev = handler;
          hlist->handlers = handler;
        }
      hlist->tail_before = handler;
    }

  if (!handler->next)
    hlist->tail_after = handler;
}

这里就有一个重要的函数:handler_list_ensure

它做如下动作:

1)首先查询一下hash table: handler_list_bsa_ht, 里面有没有instance匹配的handler_list_bsa,

if 没有

    (1)则创建一个handler_list_bsa二分查找数组,并且把handler list的signal_id作为关键Key,

    (2) 把handler list放入到这个二分数组中;

     (3)然后把这个二分数组handler_list_bsa, 插入到hash table中,此时instance作为key

else 已经存在和instance对应的handler_list_bsa二分数组

      (1) 把handler list直接插入到二分数组中

     (2)二分数组,可能在插入到hash table中

具体参考代码:

static inline HandlerList*
handler_list_ensure (guint    signal_id,
		     gpointer instance)
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  HandlerList key;
  
  key.signal_id = signal_id;
  key.handlers    = NULL;
  key.tail_before = NULL;
  key.tail_after  = NULL;
  if (!hlbsa)
    {
      hlbsa = g_bsearch_array_create (&g_signal_hlbsa_bconfig);
      hlbsa = g_bsearch_array_insert (hlbsa, &g_signal_hlbsa_bconfig, &key);
      g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
    }
  else
    {
      GBSearchArray *o = hlbsa;

      hlbsa = g_bsearch_array_insert (o, &g_signal_hlbsa_bconfig, &key);
      if (hlbsa != o)
	g_hash_table_insert (g_handler_list_bsa_ht, instance, hlbsa);
    }
  return g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key);
}

上面函数执行的是各种插入操作。

4 下面看看查询过程:

1)用instanc作为key, 在hash table中,找到handler_list_bsa数组

2)从binary searchable array中提取handler list

3) 遍历list, 找到与handler_id对应的handler

static inline HandlerList*
handler_list_lookup (guint    signal_id,
		     gpointer instance)
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  HandlerList key;
  
  key.signal_id = signal_id;
  
  return hlbsa ? g_bsearch_array_lookup (hlbsa, &g_signal_hlbsa_bconfig, &key) : NULL;
}

static Handler*
handler_lookup (gpointer instance,
		gulong   handler_id,
		guint   *signal_id_p)
{
  GBSearchArray *hlbsa = g_hash_table_lookup (g_handler_list_bsa_ht, instance);
  
  if (hlbsa)
    {
      guint i;
      
      for (i = 0; i < hlbsa->n_nodes; i++)
        {
          HandlerList *hlist = g_bsearch_array_get_nth (hlbsa, &g_signal_hlbsa_bconfig, i);
          Handler *handler;
          
          for (handler = hlist->handlers; handler; handler = handler->next)
            if (handler->sequential_number == handler_id)
              {
                if (signal_id_p)
                  *signal_id_p = hlist->signal_id;
		
                return handler;
              }
        }
    }
  
  return NULL;
}

 

5 handler_new和外界的接口:

一般在外面调用g_signal_connect_xxx()是调用:

比如:

gulong
g_signal_connect_data (gpointer       instance,
		       const gchar   *detailed_signal,
		       GCallback      c_handler,
		       gpointer       data,
		       GClosureNotify destroy_data,
		       GConnectFlags  connect_flags)
gulong
g_signal_connect_closure (gpointer     instance,
			  const gchar *detailed_signal,
			  GClosure    *closure,
			  gboolean     after) 
gulong
g_signal_connect_closure_by_id (gpointer  instance,
				guint     signal_id,
				GQuark    detail,
				GClosure *closure,
				gboolean  after)

就是说,当Caller用上面3个函数时,就会给当前的instance/signal_id,

1. 注册好callback

2. 绑定closure

3. 生成handler (handler 绑定callback/closure)

4. 放入handler list

5. 放入handler list的二分查找array中

6. 插入到全局的hash table中。

 

之后的查询函数:

handler_lookup/handler_list_lookup/handlers_find, 从会根据instance/signal_id,来找到与之对应的handlers

 

信号的发射:

1. 先定义:这其实是个链表,为啥定义2个链表呢?

static Emission      *g_recursive_emissions = NULL;
static Emission      *g_restart_emissions = NULL;

2. 再定义3个内联函数:

把临时emission, 放入全局的list中:push

查找:find

弹出临时emission: pop

static inline void		emission_push		(Emission	**emission_list_p,
							 Emission	 *emission);
static inline void		emission_pop		(Emission	**emission_list_p,
							 Emission	 *emission);
static inline Emission*		emission_find		(Emission	 *emission_list,
							 guint		  signal_id,
							 GQuark		  detail,
							 gpointer	  instance);

 

gobjec相关学习文章的list.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GObject Reference Manual

gobjec相关学习文章的list.

 

Read the document and source code.

https://developer.gnome.org/gobject/unstable/

 

Introduction
I. Concepts
Background
Data types and programming
Exporting a C API
The GLib Dynamic Type System
Copy functions
Conventions
Non-instantiable non-classed fundamental types
Instantiable classed types: objects
Initialization and Destruction
Non-instantiable classed types: interfaces
Interface Initialization
Interface Destruction
The GObject base class
Object instantiation
Object memory management
Reference count
Weak References
Reference counts and cycles
Object properties
Accessing multiple properties at once
The GObject messaging system
Closures
C Closures
Non-C closures (for the fearless)
Signals
Signal registration
Signal connection
Signal emission
Thedetail argument
II. API Reference
Type Information — The GLib Runtime type identification and management system
GTypePlugin — An interface for dynamically loadable types
GTypeModule — Type loading modules
GObject — The base object type
Enumeration and Flag Types — Enumeration and flags types
Boxed Types — A mechanism to wrap opaque C structures registered by the type system
Generic values — A polymorphic type that can hold values of any other type
Parameters and Values — Standard Parameter and Value Types
GParamSpec — Metadata for parameter specifications
Varargs Value Collection — Converting varargs to generic values
Signals — A means for customization of object behaviour and a general purpose notification mechanism
Closures — Functions as first-class objects
Value arrays — A container structure to maintain an array of generic values
GBinding — Bind two object properties
III. Tools Reference
glib-mkenums — C language enum description generation utility
glib-genmarshal — C code marshaller generation utility for GLib closures
gobject-query — display a tree of types
IV. Tutorial
How to define and implement a new GObject
Boilerplate header code
Boilerplate code
Object Construction
Object Destruction
Object methods
Non-virtual public methods
Virtual public methods
Virtual private Methods
Chaining up
How to define and implement interfaces
Defining interfaces
Implementing interfaces
Interface definition prerequisites
Interface properties
Overriding interface methods
How to create and use signals
Simple use of signals
V. Related Tools
Vala
GObject builder
Graphical inspection of GObjects
Debugging reference count problems
Writing API docs
Index
Index of deprecated symbols
Index of new symbols in 2.2
Index of new symbols in 2.4
Index of new symbols in 2.6
Index of new symbols in 2.8
Index of new symbols in 2.10
Index of new symbols in 2.12
Index of new symbols in 2.14
Index of new symbols in 2.18
Index of new symbols in 2.22
Index of new symbols in 2.24
Index of new symbols in 2.26
Index of new symbols in 2.28
Index of new symbols in 2.30
Index of new symbols in 2.32
Index of new symbols in 2.34
Index of new symbols in 2.36
Annotation Glossary

type.h

gobjec相关学习文章的list.

Read the source code!

看再多的文档,最后都要回归到code上面来。

Gtype.h:

/* Basic Type Macros

 */

#define G_TYPE_FUNDAMENTAL(type)                (g_type_fundamental (type))

#define                G_TYPE_FUNDAMENTAL_MAX                 (255 << G_TYPE_FUNDAMENTAL_SHIFT) //1020

#define G_TYPE_INVALID                                            G_TYPE_MAKE_FUNDAMENTAL (0) //0

#define G_TYPE_NONE                                                G_TYPE_MAKE_FUNDAMENTAL (1)   //4

#define G_TYPE_INTERFACE                      G_TYPE_MAKE_FUNDAMENTAL (2)   //8

#define G_TYPE_CHAR                                 G_TYPE_MAKE_FUNDAMENTAL (3)   //12

#define G_TYPE_UCHAR                                              G_TYPE_MAKE_FUNDAMENTAL (4) //16

#define G_TYPE_BOOLEAN                                         G_TYPE_MAKE_FUNDAMENTAL (5) //20

#define G_TYPE_INT                                      G_TYPE_MAKE_FUNDAMENTAL (6)  //24

#define G_TYPE_UINT                                  G_TYPE_MAKE_FUNDAMENTAL (7)

#define G_TYPE_LONG                                 G_TYPE_MAKE_FUNDAMENTAL (8)

#define G_TYPE_ULONG                                             G_TYPE_MAKE_FUNDAMENTAL (9)

#define G_TYPE_INT64                                 G_TYPE_MAKE_FUNDAMENTAL (10)

#define G_TYPE_UINT64                                              G_TYPE_MAKE_FUNDAMENTAL (11)

#define G_TYPE_ENUM                                               G_TYPE_MAKE_FUNDAMENTAL (12)

#define G_TYPE_FLAGS                                                G_TYPE_MAKE_FUNDAMENTAL (13)

#define G_TYPE_FLOAT                                                G_TYPE_MAKE_FUNDAMENTAL (14)

#define G_TYPE_DOUBLE                                            G_TYPE_MAKE_FUNDAMENTAL (15)

#define G_TYPE_STRING                                             G_TYPE_MAKE_FUNDAMENTAL (16)

#define G_TYPE_POINTER                                           G_TYPE_MAKE_FUNDAMENTAL (17)

#define G_TYPE_BOXED                                               G_TYPE_MAKE_FUNDAMENTAL (18)

#define G_TYPE_PARAM                                             G_TYPE_MAKE_FUNDAMENTAL (19)

#define G_TYPE_OBJECT                                              G_TYPE_MAKE_FUNDAMENTAL (20)

#define                G_TYPE_VARIANT                  G_TYPE_MAKE_FUNDAMENTAL (21) //84

有个疑问:为什么要shift移位?:

#define                G_TYPE_FUNDAMENTAL_SHIFT               (2)
  1. 定义Type ID时,左移;
  2. 保存指针到数组时,又右移,取得数组的index
  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;
    }
  1. 在从数组中提取时,又右移
static inline TypeNode*
lookup_type_node_I (register GType utype)
{
  if (utype > G_TYPE_FUNDAMENTAL_MAX)
    return (TypeNode*) (utype & ~TYPE_ID_MASK);
  else
    return static_fundamental_type_nodes[utype >> G_TYPE_FUNDAMENTAL_SHIFT];
}

这种左移,右移的移位操作,本身效率是高,但是为什么要这样做?

类函数何时用?

 

GType boilerplate:

模板文件,这些宏,在后面用户自定义GObject时非常有用,

要理解他们,用好他们;

 

Gtype.c:

/* --- structures --- */

struct _TypeNode

{

  guint volatile ref_count;

  GTypePlugin *plugin;

  guint        n_children; /* writable with lock */  //子辈的个数

  guint        n_supers : 8; //父辈的个数

  guint        n_prerequisites : 9;

  guint        is_classed : 1;

  guint        is_instantiatable : 1;

  guint        mutatable_check_cache : 1;   /* combines some common path checks */

GType       *children; /* writable with lock */ //指针数组,用来保持孩子的信息:pnode->children[i] = type; pnode->children = g_renew (GType, pnode->children, pnode->n_children);


  TypeData * volatile data;

  GQuark       qname;

  GData       *global_gdata;

  union {

    GAtomicArray iface_entries;                  /* for !iface types */

    GAtomicArray offsets;

  } _prot;

  GType       *prerequisites;

  GType        supers[1]; /* flexible array */ //直接用memory copy进行增长

};



#define NODE_TYPE(node)                                                         (node->supers[0]) //自己

#define NODE_PARENT_TYPE(node)                                      (node->supers[1]) //父亲

#define NODE_FUNDAMENTAL_TYPE(node)                      (node->supers[node->n_supers]) //最顶层的父亲,就是基本类型


/* --- type nodes --- */

static GHashTable       *static_type_nodes_ht = NULL;

static TypeNode                               *static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, }; //全局的指针数组,用来保存基本类型,256个

static GType                       static_fundamental_next = G_TYPE_RESERVED_USER_FIRST;


?为什么需要一个hash table和一个全局的指针数组来保存Node节点呢?

数组查找快?

下面这个函数,同时操作上面的hash table和全局数组static_fundamental_type_nodes[]


static TypeNode*

type_node_any_new_W (TypeNode             *pnode,

                                     GType                 ftype,

                                     const gchar          *name,

                                     GTypePlugin          *plugin,

                                     GTypeFundamentalFlags type_flags)

{

  guint n_supers;

  GType type;

  TypeNode *node;

  guint i, node_size = 0;


  n_supers = pnode ? pnode->n_supers + 1 : 0; //如果有父类,则父类加1,否则父类为0


  //如果没有父类,则按照基础类型大小

  if (!pnode)

node_size += SIZEOF_FUNDAMENTAL_INFO;                      /* fundamental type info */


  node_size += SIZEOF_BASE_TYPE_NODE ();            /* TypeNode structure */

node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + (0) for ->supers[] */


//给新节点分配内存

node = g_malloc0 (node_size);


//如果没有父节点,则直接保存在基础类型static_fundamental_type_nodes数组中

  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;

    }

  else

    type = (GType) node; //如果有父节点,则新分配的节点作为当前节点


  g_assert ((type & TYPE_ID_MASK) == 0);


  node->n_supers = n_supers; //记录有几个父节点

  if (!pnode)

    { //如果没有父节点

      node->supers[0] = type; //第一个节点就是,自己

      node->supers[1] = 0;

     

      node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0;

      node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0;

     

      if (NODE_IS_IFACE (node))

                {

          IFACE_NODE_N_PREREQUISITES (node) = 0;

                  IFACE_NODE_PREREQUISITES (node) = NULL;

                }

      else

                _g_atomic_array_init (CLASSED_NODE_IFACES_ENTRIES (node));

    }

  else

    { //如果有父节点

      node->supers[0] = type; //第一个节点就是自己


      //node->supers+1,指针后移动

     //把父节点的所有数据,都复制一份给当前节点,作为当前节点的父类信息

      memcpy (node->supers + 1, pnode->supers, sizeof (GType) * (1 + pnode->n_supers + 1));

     

      node->is_classed = pnode->is_classed;

      node->is_instantiatable = pnode->is_instantiatable;

     

      if (NODE_IS_IFACE (node))

                {

                  IFACE_NODE_N_PREREQUISITES (node) = 0;

                  IFACE_NODE_PREREQUISITES (node) = NULL;

                }

      else

                {

                  guint j;

                  IFaceEntries *entries;


                  entries = _g_atomic_array_copy (CLASSED_NODE_IFACES_ENTRIES (pnode),

                                                                                  IFACE_ENTRIES_HEADER_SIZE,

                                                                                  0);

                  if (entries)

                    {

                      for (j = 0; j < IFACE_ENTRIES_N_ENTRIES (entries); j++)

                                {

                                  entries->entry[j].vtable = NULL;

                                  entries->entry[j].init_state = UNINITIALIZED;

                                }

                      _g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node),

                                                                      entries);

                    }

                }

      //再和父类建立好关系

      i = pnode->n_children++;

      pnode->children = g_renew (GType, pnode->children, pnode->n_children);

      pnode->children[i] = type;

    }


  TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type));


  node->plugin = plugin;

  node->n_children = 0;

  node->children = NULL;

  node->data = NULL;

  node->qname = g_quark_from_string (name);

  node->global_gdata = NULL;


 //同时压入hash table中

  g_hash_table_insert (static_type_nodes_ht,

                                       GUINT_TO_POINTER (node->qname),

                                       (gpointer) type);

  return node;

}

上面提到的那个静态全局数组:TypeNode * static_fundamental_type_nodes[256];

TypeNode *

TypeNode *

TypeNode *

……

TypeNode *

保存的基础节点,即:没有父类的节点类型:同时,这些type也是可以做父亲的类型

|
`Void

|
`GInterface
|
`GTypePlugin
|
`gchar
|
`guchar
|
`gboolean
|
`gint
|
`guint
|
`glong
|
`gulong
|
`gint64
|
`guint64
|
`GEnum
|
`GFlags
|
`gfloat
|
`gdouble
|
`gchararray
|
`gpointer
|
`GType
|
`GBoxed
|
`GValueArray
|
`GParam
|
`GParamVariant
|
`GObject
|
`GVariant

下面是个内联函数,可以取代宏,有编译器直接展开,效率高;

同时,也可以检查参数的有效性;

static inline TypeNode*

lookup_type_node_I (register GType utype)

{

  if (utype > G_TYPE_FUNDAMENTAL_MAX)

    return (TypeNode*) (utype & ~TYPE_ID_MASK);

  else

    return static_fundamental_type_nodes[utype >> G_TYPE_FUNDAMENTAL_SHIFT];

}

数组的直接访问,效率是非常高的。

 

另外一个Hash Table:  GHashTable       *static_type_nodes_ht = NULL;

只有一个函数进行了调用:

GType

g_type_from_name (const gchar *name)

{

  GType type = 0;

  GQuark quark;


  g_return_val_if_fail (name != NULL, 0);


  quark = g_quark_try_string (name);

  if (quark)

    {

      G_READ_LOCK (&type_rw_lock);

      type = (GType) g_hash_table_lookup (static_type_nodes_ht, GUINT_TO_POINTER (quark));

      G_READ_UNLOCK (&type_rw_lock);

    }


  return type;

}

根据“名字”来查询,Hash Table的效率要高;

 

gobjec相关学习文章的list.

如何查看GType节点,以及父子关系。

gobjec相关学习文章的list.

 

使用glib的gtype/gobject开发的gtk, gstreamer等,都离不开一个gtype, gobject.

 

Glib使用2个数据结构来保存type:

//1,用指针数组,保存基础类型,即一些父类型

static TypeNode		*static_fundamental_type_nodes[(G_TYPE_FUNDAMENTAL_MAX >> G_TYPE_FUNDAMENTAL_SHIFT) + 1] = { NULL, };

//2, 用散列表来保持所有的类型,包括父类型,子类型

static GHashTable       *static_type_nodes_ht = NULL;
static_type_nodes_ht = g_hash_table_new (g_direct_hash, g_direct_equal);

它使用指针作为键值

 

type创建的过程:

 

  1. 定义xxx_yyy_get_type(), 一般是用宏来定义的
  2. Init时,会调用g_type_class_ref(xxx_yyy_get_type())

如:

g_type_class_ref (gst_object_get_type ());

g_type_class_ref (gst_pad_get_type ());

g_type_class_ref (gst_element_factory_get_type ());

g_type_class_ref (gst_element_get_type ());

  1. xxx_yyy_get_type()会进行type的创建,保存到指针数组和hash table中:
  2. g_type_register_static()
  3. type_node_any_new_W() 首先检查该新建的type,是否由父亲;

如果没有的话,保持在static_fundamental_type_nodes;

如果有父类的话,就保持到父类的子类数组中pnode->children[i] = type;

同时把这个新的type 插入到hash table中:

g_hash_table_insert (static_type_nodes_ht,

GUINT_TO_POINTER (node->qname),

(gpointer) type);

 

如果查看type的list, 以及父子关系呢:

参考gobject_query.c

主要是把父子关系清晰的打印出来:

指针数组static_fundamental_type_nodes,存放了无父类的typenode;

所以只要循环这个指针数组,然后对基础typenode, 嵌套循环其子类,就可以了。

/* GObject - GLib Type, Object, Parameter and Signal Library
 * Copyright (C) 1998-1999, 2000-2001 Tim Janik and Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#include <stdlib.h>
#include <string.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>

#include <glib-object.h>
#include <glib/gprintf.h>

static GType root = G_TYPE_OBJECT;

static void
show_nodes (GType        type,
	    GType        sibling,
	    const gchar *indent)
{
	static gchar *indent_inc = NULL;
	static guint spacing = 1;
	static gboolean recursion = TRUE;

#define	O_SPACE	" "
#define	O_ESPACE ""
#define	O_BRANCH "+"
#define	O_VLINE "|"
#define	O_LLEAF	"`"
#define	O_KEY_FILL "_"

	GType   *children;
	guint i;

	if (!type)
		return;

	if (!indent_inc)
	{
		indent_inc = g_new (gchar, strlen (O_SPACE) + 1);
		*indent_inc = 0;
		strcpy (indent_inc, O_SPACE);
	}  
  
	children = g_type_children (type, NULL);

	if (type != root)
		for (i = 0; i < spacing; i++)
			fprintf(stderr, "%s%s\n", indent, O_VLINE);
  
	fprintf(stderr, "%s%s%s%s",
				indent,
				sibling ? O_BRANCH : (type != root ? O_LLEAF : O_SPACE),
				O_ESPACE,
				g_type_name (type));

	for (i = strlen (g_type_name (type)); i <= strlen (indent_inc); i++)
		fputs (O_KEY_FILL, stdout);
	fputc ('\n', stdout);

	if (children && recursion)
	{
		gchar *new_indent;
		GType   *child;

		if (sibling)
			new_indent = g_strconcat (indent, O_VLINE, indent_inc, NULL);
		else
			new_indent = g_strconcat (indent, O_SPACE, indent_inc, NULL);

		for (child = children; *child; child++)
			show_nodes (child[0], child[1], new_indent);

		g_free (new_indent);
	}

	g_free (children);
}

void list_type_node_tree(void)
{
	gint i;
	
  	root = ~0;
	for (i = 0; i <= G_TYPE_FUNDAMENTAL_MAX; i += G_TYPE_MAKE_FUNDAMENTAL (1))
	{
		const gchar *name = g_type_name (i);

		if (name)
			show_nodes (i, 0, "");
	}

}

g_type_init()后的type list, 以及父子关系:


id

|

`GInterface

  |

  `GTypePlugin

|

`gchar

|

`guchar

|

`gboolean

|

`gint

|

`guint

|

`glong

|

`gulong

|

`gint64

|

`guint64

|

`GEnum

|

`GFlags

|

`gfloat

|

`gdouble

|

`gchararray

|

`gpointer

  |

  `GType

|

`GBoxed

  |

  `GValueArray

|

`GParam

  |

  +GParamChar

  |

  +GParamUChar

  |

  +GParamBoolean

  |

  +GParamInt

  |

  +GParamUInt

  |

  +GParamLong

  |

  +GParamULong

  |

  +GParamInt64

  |

  +GParamUInt64

  |

  +GParamUnichar

  |

  +GParamEnum

  |

  +GParamFlags

  |

  +GParamFloat

  |

  +GParamDouble

  |

  +GParamString

  |

  +GParamParam

  |

  +GParamBoxed

  |

  +GParamPointer

  |

  +GParamValueArray

  |

  +GParamObject

  |

  +GParamOverride

  |

  +GParamGType

  |

  `GParamVariant

|

`GObject

|

`GVariant

 

gst_init()后 type/gobject list, 以及object的父子关系:

|

`void

|

`GInterface

  |

  +GTypePlugin

  |

  +GstChildProxy

  |

  `GstURIHandler

|

`gchar

|

`guchar

|

`gboolean

|

`gint

|

`guint

|

`glong

|

`gulong

|

`gint64

|

`guint64

|

`GEnum

  |

  +GstFormat

  |

  +GstPadDirection

  |

  +GstBufferListItem

  |

  +GstBusSyncReply

  |

  +GstClockReturn

  |

  +GstClockEntryType

  |

  +GstClockType

  |

  +GstState

  |

  +GstStateChangeReturn

  |

  +GstStateChange

  |

  +GstCoreError

  |

  +GstLibraryError

  |

  +GstResourceError

  |

  +GstStreamError

  |

  +GstEventType

  |

  +GstSeekType

  |

  +GstQOSType

  |

  +GstIndexCertainty

  |

  +GstIndexEntryType

  |

  +GstIndexLookupMethod

  |

  +GstIndexResolverMethod

  |

  +GstDebugLevel

  |

  +GstDebugColorFlags

  |

  +GstIteratorResult

  |

  +GstIteratorItem

  |

  +GstPadLinkReturn

  |

  +GstFlowReturn

  |

  +GstActivateMode

  |

  +GstPadPresence

  |

  +GstPluginError

  |

  +GstRank

  |

  +GstQueryType

  |

  +GstBufferingMode

  |

  +GstStreamStatusType

  |

  +GstStructureChangeType

  |

  +GstTagMergeMode

  |

  +GstTagFlag

  |

  +GstTaskState

  |

  +GstTypeFindProbability

  |

  +GstURIType

  |

  +GstParseError

  |

  +GstSearchMode

  |

  +GstProgressType

  |

  `GstCapsIntersectMode

|

`GFlags

  |

  +GstObjectFlags

  |

  +GstBinFlags

  |

  +GstBufferFlag

  |

  +GstBufferCopyFlags

  |

  +GstBusFlags

  |

  +GstCapsFlags

  |

  +GstClockFlags

  |

  +GstDebugGraphDetails

  |

  +GstElementFlags

  |

  +GstEventTypeFlags

  |

  +GstSeekFlags

  |

  +GstAssocFlags

  |

  +GstIndexFlags

  |

  +GstMessageType

  |

  +GstMiniObjectFlags

  |

  +GstPadLinkCheck

  |

  +GstPadFlags

  |

  +GstPadTemplateFlags

  |

  +GstPipelineFlags

  |

  +GstPluginFlags

  |

  +GstPluginDependencyFlags

  |

  +GstAllocTraceFlags

  |

  `GstParseFlags

|

`gfloat

|

`gdouble

|

`gchararray

|

`gpointer

  |

  `GType

|

`GBoxed

  |

  +GValueArray

  |

  +GstCaps

  |

  +GstStructure

  |

  +GstDate

  |

  +GstDateTime

  |

  +GDate

  |

  +GstBufferListIterator

  |

  +GstParseContext

  |

  `GError

|

`GParam

  |

  +GParamChar

  |

  +GParamUChar

  |

  +GParamBoolean

  |

  +GParamInt

  |

  +GParamUInt

  |

  +GParamLong

  |

  +GParamULong

  |

  +GParamInt64

  |

  +GParamUInt64

  |

  +GParamUnichar

  |

  +GParamEnum

  |

  +GParamFlags

  |

  +GParamFloat

  |

  +GParamDouble

  |

  +GParamString

  |

  +GParamParam

  |

  +GParamBoxed

  |

  +GParamPointer

  |

  +GParamValueArray

  |

  +GParamObject

  |

  +GParamOverride

  |

  +GParamGType

  |

  +GParamVariant

  |

  `GstParamFraction

|

`GObject

  |

  +GstObject

  | |

  | +GstPad

  | |

  | +GstPadTemplate

  | |

  | +GstPluginFeature

  | | |

  | | +GstElementFactory

  | | |

  | | +GstTypeFindFactory

  | | |

  | | `GstIndexFactory

  | |

  | +GstElement

  | | |

  | | `GstBin

  | |   |

  | |   `GstPipeline

  | |

  | +GstBus

  | |

  | +GstTask

  | |

  | +GstTaskPool

  | |

  | +GstClock

  | |

  | +GstPlugin

  | |

  | `GstRegistry

  |

  `GstSignalObject

|

`GVariant

|

`GstMiniObject

  |

  +GstQuery

  |

  +GstMessage

  |

  +GstBuffer

  |

  +GstEvent

  |

  `GstBufferList

|

`GstFourcc

|

`GstIntRange

|

`GstInt64Range

|

`GstDoubleRange

|

`GstFractionRange

|

`GstValueList

|

`GstValueArray

|

`GstFraction

gobjec相关学习文章的list.