Closures是异步信号传递概念的中心,它被广泛的应用到了GTK+和GNOME应用程序中。一个Closure是一个抽象的、通用表示的回调(callback)。它是一个包含三个对象的简单结构:
GClosure结构代表所有Closure实现的共同基础:现在存在着很多各自不同的使用GObject类型系统的运行环境。GObject库提供一个简单的GCClosure类型,来明确使用C/C++的回调来实现。
一个GClosure提供以下简单的服务:
如果你用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函数如果用户提供过的话。
我们在前面提到过,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对象。
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
1. New 一个Handler
2. New 一个closure, 并让closure持有用户自己的callback: C Handler [User Handler]
3. 把handler放入全局的handler_list
4. 指定closure的marshaller:
handler->closure->marshal = node->marshaller;
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 |
上层: Caller (Signal connect + user callback )
下层: Callee (Signal New + Emission机制)
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:
比如: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
在某种条件下,会调用
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语言的形式。
我们从一个实际的例子看看:
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
1. Signal Node生成
2. callback指定
3. c Marshal指定
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); }
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);
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
转载时请注明出处和作者联系方式
作者联系方式:会飞的鱼 <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); }; |
|
数据定义 |
说明 |
gtype |
一个全局指针数组, 用来保存基本类型; 通过基本类型顺藤摸瓜,可以找到其子类型 |
|
static GHashTable *static_type_nodes_ht = NULL; |
一个全局的Hash Table 这个Hash Table一般是通过“名字”来查询TypeNode时使用 |
|
|
|
|
Signal |
双指针 == 指针数组? |
|
static GBSearchArray *g_signal_key_bsa = NULL; | 方便根据signal name/type来查询signal_id | |
|
信号处理函数用hash table |
|
|
|
|
|
static Emission *g_restart_emissions = NULL; |
|
|
||
object |
这个和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 |
用Hash Table来保存Object的ParamSpec |
|
|
/* --- type initialization --- */ |
一个全局数组 |
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; }
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.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
安装属性,其实就是把属性放到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的回调,实际上是为了处理用户定义的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);
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; }
上面定义的数组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); }
这里的处理有点意思,也不知道到底是否高效:
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);
Read the document and source code.
https://developer.gnome.org/gobject/unstable/
Read the source code!
看再多的文档,最后都要回归到code上面来。
/* 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
#define G_TYPE_FUNDAMENTAL_SHIFT (2)
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; }
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]; } 这种左移,右移的移位操作,本身效率是高,但是为什么要这样做? |
模板文件,这些宏,在后面用户自定义GObject时非常有用,
要理解他们,用好他们;
/* --- 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的效率要高;
使用glib的gtype/gobject开发的gtk, gstreamer等,都离不开一个gtype, gobject.
//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);
它使用指针作为键值
如:
如果没有的话,保持在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); |
参考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
| `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