Gobject tutorial 九

ops/2024/11/14 0:15:43/

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 reasonsas 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是以他们连接到信号的先后顺序执行的。


http://www.ppmy.cn/ops/50849.html

相关文章

视频媒介VS文字媒介

看到一篇蛮有思考意义的文章就摘录下来了&#xff0c;也引起了反思 目录 一、视频的定义 二、”视频媒介“与”文字媒介”作对比 1.形象 VS 抽象 2.被动 VS 主动 三、视频的缺点-【更少】的思考 1.看视频为啥会导致【更少的思考】 2.内容的【浅薄化】 3.内容的【娱乐化…

算法设计与分析 笔记

截图摘自湖南大学彭鹏老师的ppt。笔记也是根据他的ppt整理的。 动态规划 核心 用数组记录中间结果&#xff0c;避免重复计算 三角数塔问题 问题描述 给定一个三角形数塔&#xff0c;从顶部出发&#xff0c;每次只能移动到下一行的相邻元素。要求找到一条路径&#xff0c;…

VMware安装及创建虚拟机

安装完成后&#xff0c;点击创建新的虚拟机 操作完成后就安装成功啦 &#xff0c;下个教程出虚拟机Linux和xshell的连接及可能出现的问题解决方案

探索图神经网络(GNN):使用Python实现你的GNN模型

一、引言 图神经网络&#xff08;Graph Neural Network, GNN&#xff09;作为近年来机器学习和深度学习领域的热门话题&#xff0c;正逐渐吸引越来越多的研究者和开发者的关注。GNN能够处理图结构数据&#xff0c;在社交网络分析、推荐系统、化学分子结构预测等领域有着广泛的…

vscode 调试

VScode 调试教程 tasks.json和launch.json的设置&#xff08;超详细&#xff09;_vscode launch.json在哪-CSDN博客 launch.json {// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, v…

Modbus协议转Profibus协议网关模块连PLC与激光发射器通讯

一、概述 在PLC控制系统中&#xff0c;从站设备通常以Modbus协议&#xff0c;ModbusTCP协议&#xff0c;Profinet协议&#xff0c;Profibus协议&#xff0c;Profibus DP协议&#xff0c;EtherCAT协议&#xff0c;EtherNET协议等。本文将重点探讨PLC连接Modbus协议转Profibus协…

行列视(RCV)处理关系数据库的机制探讨

行列视&#xff08;RCV&#xff09;处理关系数据库的机制&#xff0c;是其核心功能之一&#xff0c;为生产型企业提供了强大的数据分析和管理工具。下面将深入探讨RCV如何处理关系数据库&#xff0c;包括其处理机制、特点和优势等方面。 首先&#xff0c;RCV处理关系数据库的核…

Linux权限提升四

➢ Linux系统提权-Web&用户-数据库类型 ➢ Linux系统提权-Web&用户-Capability能力 ➢ Linux系统提权-普通用户-LD_Preload加载 #Linux系统提权-Web&用户-数据库类型 https://www.vulnhub.com/entry/raven-2,269/ 1、信息收集&#xff1a; http://192.168.139.155/…