Gobject tutorial 九

The GObject messaging system

Closures

closure是一个抽象、通用的概念,它包含一个函数和一些变量。作用就是实现函数回调功能。

我们看看GLib对closure是怎么定义的。

/**
 * GClosure:
 * @in_marshal: Indicates whether the closure is currently being invoked with 
 *  g_closure_invoke()
 * @is_invalid: Indicates whether the closure has been invalidated by 
 *  g_closure_invalidate()
 * 
 * A #GClosure represents a callback supplied by the programmer.
 */
struct _GClosure
{
  /*< private >*/
  guint ref_count : 15;  /* (atomic) */
  /* meta_marshal is not used anymore but must be zero for historical reasons
     as it was exposed in the G_CLOSURE_N_NOTIFIERS macro */
  guint meta_marshal_nouse : 1;  /* (atomic) */
  guint n_guards : 1;  /* (atomic) */
  guint n_fnotifiers : 2;  /* finalization notifiers (atomic) */
  guint n_inotifiers : 8;  /* invalidation notifiers (atomic) */
  guint in_inotify : 1;  /* (atomic) */
  guint floating : 1;  /* (atomic) */
  /*< protected >*/
  guint derivative_flag : 1;  /* (atomic) */
  /*< public >*/
  guint in_marshal : 1;  /* (atomic) */
  guint is_invalid : 1;  /* (atomic) */

  /*< private >*/	void   (*marshal)  (GClosure       *closure,
					    GValue /*out*/ *return_value,
					    guint           n_param_values,
					    const GValue   *param_values,
					    gpointer        invocation_hint,
					    gpointer	    marshal_data);
  /*< protected >*/	gpointer data;

  /*< private >*/	GClosureNotifyData *notifiers;

  /* invariants/constraints:
   * - ->marshal and ->data are _invalid_ as soon as ->is_invalid==TRUE
   * - invocation of all inotifiers occurs prior to fnotifiers
   * - order of inotifiers is random
   *   inotifiers may _not_ free/invalidate parameter values (e.g. ->data)
   * - order of fnotifiers is random
   * - each notifier may only be removed before or during its invocation
   * - reference counting may only happen prior to fnotify invocation
   *   (in that sense, fnotifiers are really finalization handlers)
   */
};

对于不同的runtime,closure有不同具体实现,对于C/C++而言,这个具体的实现就是GCClosure.

其定义如下:

struct _GCClosure
{
  GClosure	closure;
  gpointer	callback;
};

 g_cclosure_new、g_cclosure_new_swap都是用于创建closure的函数,创建完成的closure会以用户提供的数据为参数,调用用户提供的callback_func。二者的不同在于,用户提的的数据作为参数传递给函数时,数据在参数中的位置不同,对于g_cclosure_new,用户的参数是作为最后一个参数,而对于g_cclosure_new_swap,用户的数据作为第一个参数的。

我们注意到,在GClosure的定义中,有一个函数指针,marshaller,对于C语言来说,这个函数的作用是将一组GValue转换为c格式的参数列表,这组GValue代表的是即将传递给回调函数的参数。当参数转换完成后,marhaaller还会调用用户提供的回调函数,并以刚刚转换完成的参数列表作为参数。当回调函数执行完成后,有将函数的返回结果转换成GValue类型,并将GValue返回到marshaller的调用者。

Glib中有一个通用的函数,g_cclosure_marshal_generic,我们在平常在调用g_signal_new时,当将其参数c_marshaller设置为NULL时,g_cclosure_marshal_generic就是此时的默认marshaller.当然,还存在其他的marshaller,比如说g_cclosure_marshal_VOID__INT。

我们以g_cclosure_marshal_generic为例,看看marshaller的执行流程

/**
 * g_cclosure_marshal_generic:
 * @closure: A #GClosure.
 * @return_gvalue: A #GValue to store the return value. May be %NULL
 *   if the callback of closure doesn't return a value.
 * @n_param_values: The length of the @param_values array.
 * @param_values: An array of #GValues holding the arguments
 *   on which to invoke the callback of closure.
 * @invocation_hint: The invocation hint given as the last argument to
 *   g_closure_invoke().
 * @marshal_data: Additional data specified when registering the
 *   marshaller, see g_closure_set_marshal() and
 *   g_closure_set_meta_marshal()
 *
 * A generic marshaller function implemented via
 * [libffi](http://sourceware.org/libffi/).
 *
 * Normally this function is not passed explicitly to g_signal_new(),
 * but used automatically by GLib when specifying a %NULL marshaller.
 *
 * Since: 2.30
 */
void
g_cclosure_marshal_generic (GClosure     *closure,
                            GValue       *return_gvalue,
                            guint         n_param_values,
                            const GValue *param_values,
                            gpointer      invocation_hint,
                            gpointer      marshal_data)
{
  ffi_type *rtype;
  void *rvalue;
  int n_args;
  ffi_type **atypes;
  void **args;
  int i;
  ffi_cif cif;
  GCClosure *cc = (GCClosure*) closure;
  gint *enum_tmpval;
  gboolean tmpval_used = FALSE;

  enum_tmpval = g_alloca (sizeof (gint));
  if (return_gvalue && G_VALUE_TYPE (return_gvalue))
    {
      rtype = value_to_ffi_type (return_gvalue, &rvalue, enum_tmpval, &tmpval_used);
    }
  else
    {
      rtype = &ffi_type_void;
    }

  rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg)));

  n_args = n_param_values + 1;
  atypes = g_alloca (sizeof (ffi_type *) * n_args);
  args =  g_alloca (sizeof (gpointer) * n_args);

  if (tmpval_used)
    enum_tmpval = g_alloca (sizeof (gint));

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      atypes[n_args-1] = value_to_ffi_type (param_values + 0,
                                            &args[n_args-1],
                                            enum_tmpval,
                                            &tmpval_used);
      atypes[0] = &ffi_type_pointer;
      args[0] = &closure->data;
    }
  else
    {
      atypes[0] = value_to_ffi_type (param_values + 0,
                                     &args[0],
                                     enum_tmpval,
                                     &tmpval_used);
      atypes[n_args-1] = &ffi_type_pointer;
      args[n_args-1] = &closure->data;
    }

  for (i = 1; i < n_args - 1; i++)
    {
      if (tmpval_used)
        enum_tmpval = g_alloca (sizeof (gint));

      atypes[i] = value_to_ffi_type (param_values + i,
                                     &args[i],
                                     enum_tmpval,
                                     &tmpval_used);
    }

  if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
    return;

  ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);

  if (return_gvalue && G_VALUE_TYPE (return_gvalue))
    value_from_ffi_type (return_gvalue, rvalue);
}

Signals

在之前的例子中,我们在应用程序中使用过signal, 接下来,我们深入聊聊signal背后的逻辑。

信号的作用是连接特定用户事件与事件监听者。例如,在GTK中, 当窗口系统收到用户事件(比如键盘敲击事件、鼠标移动事件),窗口系统会生成GTK事件,并通过信号的形式发送到widget对象实例。

信号会在注册过程中会与具体的对象类型进行绑定,这意味着此对象类型会发出这个信号。在父对象类型注册的信号在其子对象类型也能使用。信号发生过程中会使用到closure.用户能通过连接到信号的closure控制发出或者停止发出信号。当一个类型实例上有信号发出,这个类型实例上所有连接到此信号的closure都会被调用。

为一个存在的对象类型注册新信号时,会使用函数g_signal_newv()、g_signal_new_valist()以及g_signal_new()。g_signal_new_valist()和g_signal_new()都是通过g_signal_newv()来实现的,我们看看GLib中g_signal_newv()中的声明。

/**
 * g_signal_newv:
 * @signal_name: the name for the signal
 * @itype: the type this signal pertains to. It will also pertain to
 *     types which are derived from this type
 * @signal_flags: a combination of #GSignalFlags specifying detail of when
 *     the default handler is to be invoked. You should at least specify
 *     %G_SIGNAL_RUN_FIRST or %G_SIGNAL_RUN_LAST
 * @class_closure: (nullable): The closure to invoke on signal emission;
 *     may be %NULL
 * @accumulator: (nullable): the accumulator for this signal; may be %NULL
 * @accu_data: (nullable) (closure accumulator): user data for the @accumulator
 * @c_marshaller: (nullable): the function to translate arrays of
 *     parameter values to signal emissions into C language callback
 *     invocations or %NULL
 * @return_type: the type of return value, or %G_TYPE_NONE for a signal
 *     without a return value
 * @n_params: the length of @param_types
 * @param_types: (array length=n_params) (nullable): an array of types, one for
 *     each parameter (may be %NULL if @n_params is zero)
 *
 * Creates a new signal. (This is usually done in the class initializer.)
 *
 * See g_signal_new() for details on allowed signal names.
 *
 * If c_marshaller is %NULL, g_cclosure_marshal_generic() will be used as
 * the marshaller for this signal.
 *
 * Returns: the 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)

可以看出,信号基本就是对连接到信号的closure的描述。

信号在发送过程中会调用一系列的回调函数。这些回调函数主要分为两类:per-object类和用户提供的。per-object类回调函数通常称为"object method handler" 或者"default(signal)handler",用户提供的回调函数通常称为“signal handler"。

所有的handler都将对象类型实例指针作为第一个参数,将一个gpointer user_data作为最后一个参数,signal-define 参数在两者之间,user_data中通常会有用户在连接handler到信号时所提供的数据。回调函数的类型是GCallback。

一个正常的信号发送过程包含五个阶段,除非中间的过程被打断。

  1. 为设置了G_SIGNAL_RUN_FIRST flag的信号调用 default handler
  2. 调用用户提供的signal handler(signal handler 不是通过g_signal_connect_after与信号进行连接的)
  3. 为设置了G_SIGNAL_RUN_LAST flag的信号调用default handler
  4. 调用用户提供的signal handler(signal handler 不是通过g_signal_connect_after与信号进行连接的)
  5. 为设置了G_SIGNAL_RUN_CLEANUP flag的信号调用default handler

用户提供的signal handler是以他们连接到信号的先后顺序执行的。

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值