当前位置: 首页 > news >正文

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

相关文章:

  • C++ GPU编程(英伟达CUDA)
  • AVL许可证更新
  • 无需科学上网:轻松实现国内使用Coze.com平台自己创建的Bot(如何实现国内免费使用GPT-4o/Gemini等最新大模型)
  • jigdo无法下载的文件
  • 数据库讲解---(数据库保护)【下】
  • spring-boot-devtools热部署功能集成使用
  • P3056 [USACO12NOV] Clumsy Cows S
  • 智慧园区数字化能源云平台的多元化应用场景,您知道哪些?
  • 虚拟专用网络 之 VPN
  • 国有企业数字化转型常见思考框架与路线图
  • golang中的内存缓存如何避免被GC扫描,BigCache实现原理
  • 浅谈微服务架构中实现单点登录
  • WHAT - HTTP keep-alive 持久性连接和内存泄漏问题
  • 何在 Vue3 中使用 Cytoscape.js 创建交互式网络图
  • 前端面试题(六)答案版
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 《深入 React 技术栈》
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Android组件 - 收藏集 - 掘金
  • ES6语法详解(一)
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • JavaScript学习总结——原型
  • leetcode46 Permutation 排列组合
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Linux链接文件
  • magento 货币换算
  • magento2项目上线注意事项
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • OSS Web直传 (文件图片)
  • PermissionScope Swift4 兼容问题
  • Python socket服务器端、客户端传送信息
  • 当SetTimeout遇到了字符串
  • 后端_MYSQL
  • 欢迎参加第二届中国游戏开发者大会
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 基于axios的vue插件,让http请求更简单
  • 前嗅ForeSpider教程:创建模板
  • 如何用vue打造一个移动端音乐播放器
  • 线性表及其算法(java实现)
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • # windows 运行框输入mrt提示错误:Windows 找不到文件‘mrt‘。请确定文件名是否正确后,再试一次
  • $forceUpdate()函数
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (C语言)逆序输出字符串
  • (day18) leetcode 204.计数质数
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (pycharm)安装python库函数Matplotlib步骤
  • (四)图像的%2线性拉伸
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (原創) 未来三学期想要修的课 (日記)
  • (转)socket Aio demo