The GObject messaging system
Closures
C Closures
Non-C closures (for the fearless)
Signals
Signal registration
Signal connection
Signal emission
The detail argument
Closures 闭包
Closures are central to the concept of asynchronous signal delivery which is widely used throughout GTK+ and GNOME applications. A closure is an abstraction, a generic representation of a callback. It is a small structure which contains three objects:
闭包是在GTK+和GNOME应用程序中被广泛使用的异步信号传输机制的核心概念。闭包是一个函数回调的抽象的、通用的表示。它是一个包含如下三个对象的小结构:
- a function pointer (the callback itself) whose prototype looks like:
1 | return_type function_callback (… , gpointer user_data); |
-
the
user_data
pointer which is passed to the callback upon invocation of the closure
-
a function pointer which represents the destructor of the closure: whenever the closure's refcount reaches zero, this function will be called before the closure structure is freed.
The GClosure structure represents the common functionality of all closure implementations: there exists a different closure implementation for each separate runtime which wants to use the GObject type system. [4] The GObject library provides a simple GCClosure type which is a specific implementation of closures to be used with C/C++ callbacks.
GClosure结构代表了所有闭包实现的通用功能:对使用GObject类型系统的每一个运行时都有一个不同的闭包实现。GObject库提供了一个用于C/C++回调的简单GCClosure类型的实现。
A GClosure provides simple services:
-
Invocation (
g_closure_invoke
): this is what closures were created for: they hide the details of callback invocation from the callback invoker. -
Notification: the closure notifies listeners of certain events such as closure invocation, closure invalidation and closure finalization. Listeners can be registered with
g_closure_add_finalize_notifier
(finalization notification),g_closure_add_invalidate_notifier
(invalidation notification) andg_closure_add_marshal_guards
(invocation notification). There exist symmetric deregistration functions for finalization and invalidation events (g_closure_remove_finalize_notifier
andg_closure_remove_invalidate_notifier
) but not for the invocation process. [5]
g_closure_remove_finalize_notifier
和
g_closure_remove_invalidate_notifier
),但没有用于解除调用通知注册的函数。
C Closures C闭包
If you are using C or C++ to connect a callback to a given event, you will either use simple GCClosures which have a pretty minimal API or the even simpler g_signal_connect
functions (which will be presented a bit later).
如果你在使用C或/C++来连接一个指定事件的回调,你可以使用拥有简单API的简单的GCClosure甚至是更简单的g_signal_connect函数(在后面会介绍)。
g_cclosure_new
will create a new closure which can invoke the user-provided callback_func with the user-provided user_data
as its last parameter. When the closure is finalized (second stage of the destruction process), it will invoke the destroy_data
function if the user has supplied one.
g_cclosure_new
可以创建一个可以调用用户提供的回调callback_func,并将用户提供的数据user_data作为回调的最后一个参数的闭包。在闭包被终结(析构的第二个阶段)时,它会调用destroy_data函数如果用户提供了的话。
g_cclosure_new_swap
will create a new closure which can invoke the user-provided callback_func
with the user-provided user_data
as its first parameter (instead of being the last parameter as with g_cclosure_new
). When the closure is finalized (second stage of the destruction process), it will invoke the destroy_data
function if the user has supplied one.
g_cclosure_new_swap可以创建一个可以把用户提供的user_data作为回调callback_func的第一个参数(而不是像g_cclosure_new一样作为最后一个参数)的新闭包。在闭包被终结(析构的第二个阶段)时,它会调用destroy_data函数如果用户提供了的话。
Non-C closures (for the fearless) 非C闭包(为无畏者)
As was explained above, closures hide the details of callback invocation. In C, callback invocation is just like function invocation: it is a matter of creating the correct stack frame for the called function and executing a call assembly instruction.
如上所述,闭包隐藏了回调调用的细节。在C中,回调调用就像一个函数调用:为被调用函数创建一个正确的栈然后执行调用的汇编指令。
C closure marshallers transform the array of GValues which represent the parameters to the target function into a C-style function parameter list, invoke the user-supplied C function with this new parameter list, get the return value of the function, transform it into a GValue and return this GValue to the marshaller caller.
C闭包的调用器将代表目标函数的参数的GValue转换为C格式的参数列表,然后用新的参数列表调用用户提供的C函数,获取返回值后将返回值转换为一个GValue,然后把这个GValue传递给调用器的调用者。
A generic C closure marshaller is available as g_cclosure_marshal_generic
which implements marshalling for all function types using libffi. Custom marshallers for different types are not needed apart from performance critical code where the libffi-based marshaller may be too slow.
一个通用的C闭包调用器由 g_cclosure_marshal_generic
提供,它实现了所有使用libffi的函数类型的闭包调用。一般不需要为不同类型的函数使用不同的自定义调用器,除非有严格的性能要求,因为基于libffi的调用器有点慢。
An example of a custom marshaller is given below, illustrating how GValues can be converted to a C function call. The marshaller is for a C function which takes an integer as its first parameter and returns void.
下面是一个自定义的闭包调用器,说明了一个GValue怎么转换为一个C函数调用。这个调用器用于接受一个整数作为第一个参数并且没有返回值的C函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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); } |
There exist other kinds of marshallers, for example there is a generic Python marshaller which is used by all Python closures (a Python closure is used to invoke a callback written in Python). This Python marshaller transforms the input GValue list representing the function parameters into a Python tuple which is the equivalent structure in Python.
还有许多其他类型的调用器,例如有一个被所有Python闭包(Python闭包用于调用一个用Python写的回调)所使用的通用的Python调用器。Python调用器把作为函数参数的GValue列表转换为在Python中等效的Python元组。
[4] In practice, closures sit at the boundary of language runtimes: if you are writing Python code and one of your Python callbacks receives a signal from a GTK+ widget, the C code in GTK+ needs to execute your Python code. The closure invoked by the GTK+ object invokes the Python callback: it behaves as a normal C object for GTK+ and as a normal Python object for Python code.
注4 在实践上,闭包处于不同语言运行时的分界线上:如果你在开发Python代码并且你的一个Python回调从GTK+控件中收到了一个信号,在C代码中GTK+需要执行你的Python代码,被GTK+对象调用的闭包调用Python回调:在GTK+中它表现为一个通常的C对象而在Python中它表现为一个通常的Python对象。
[5] Closures are reference counted and notify listeners of their destruction in a two-stage process: the invalidation notifiers are invoked before the finalization notifiers.
注5 闭包是引用计数的,并且在析构的时候分两个阶段通知监听者:失效通知,然后是终结通知。
Signals 信号
GObject's signals have nothing to do with standard UNIX signals: they connect arbitrary application-specific events with any number of listeners. For example, in GTK+, every user event (keystroke or mouse move) is received from the windowing system and generates a GTK+ event in the form of a signal emission on the widget object instance.
GObject的信号与标准的UNIX信号没有关系:它们用于为任意数量的监听者连接任意应用特定的事件。例如在GTK+中每一个用户事件(如键盘按键或鼠标移动)将从窗口系统中接收,然后以在控件对象实例上发送信号的方式产生GTK+事件。
Each signal is registered in the type system together with the type on which it can be emitted: users of the type are said to connect to the signal on a given type instance when they register a closure to be invoked upon the signal emission. Users can also emit the signal by themselves or stop the emission of the signal from within one of the closures connected to the signal.
每一个信号连同发射它们的类型一起注册在类型系统中:当注册一个在信号发射时被调用的闭包时,我们说用户连接给定类型实例的信号。用户也可以自己发射信号,或者在连接到这些信号的任一个闭包中中止信号的发射。
When a signal is emitted on a given type instance, all the closures connected to this signal on this type instance will be invoked. All the closures connected to such a signal represent callbacks whose signature looks like:
当给定实例的信号被发射时,所有连接到该实例的信号的闭包都将被调用。所有连接到信号的闭包代表签名为如下的一个函数:
1 | return_type function_callback (gpointer instance, …, gpointer user_data); |
Signal registration 信号注册
To register a new signal on an existing type, we can use any of g_signal_newv
, g_signal_new_valist
or g_signal_new
functions:
要在一个已存在的类型上注册一个信号,我们可以使用g_signal_newv
, g_signal_new_valist
或 g_signal_new 函数:
1 2 3 4 5 6 7 8 9 10 | 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); |
The number of parameters to these functions is a bit intimidating but they are relatively simple:
这些函数的参数数量也许有些吓人,但它们其实很简单:
-
signal_name
: is a string which can be used to uniquely identify a given signal.
-
itype
: is the instance type on which this signal can be emitted.
-
signal_flags
: partly defines the order in which closures which were connected to the signal are invoked.
-
class_closure
: this is the default closure for the signal: if it is not NULL upon the signal emission, it will be invoked upon this emission of the signal. The moment where this closure is invoked compared to other closures connected to that signal depends partly on the signal_flags.
-
accumulator
: this is a function pointer which is invoked after each closure has been invoked. If it returns FALSE, signal emission is stopped. If it returns TRUE, signal emission proceeds normally. It is also used to compute the return value of the signal based on the return value of all the invoked closures. For example, an accumulator could ignoreNULL
returns from closures; or it could build a list of the values returned by the closures.
-
accu_data
: this pointer will be passed down to each invocation of the accumulator during emission.
-
c_marshaller
: this is the default C marshaller for any closure which is connected to this signal.
-
return_type
: this is the type of the return value of the signal.
-
n_params
: this is the number of parameters this signal takes.
-
param_types
: this is an array of GTypes which indicate the type of each parameter of the signal. The length of this array is indicated by n_params.
As you can see from the above definition, a signal is basically a description of the closures which can be connected to this signal and a description of the order in which the closures connected to this signal will be invoked.
如上所述,信号基本上可以认为使一个对可以连接到这个信号的闭包以及闭包调用顺序的描述。
Signal connection 信号连接
If you want to connect to a signal with a closure, you have three possibilities:
-
You can register a class closure at signal registration: this is a system-wide operation. i.e.: the class closure will be invoked during each emission of a given signal on any of the instances of the type which supports that signal.
-
You can use
g_signal_override_class_closure
which overrides the class closure of a given type. It is possible to call this function only on a derived type of the type on which the signal was registered. This function is of use only to language bindings. -
You can register a closure with the
g_signal_connect
family of functions. This is an instance-specific operation: the closure will be invoked only during emission of a given signal on a given instance.
g_signal_connect家族函数注册一个闭包,这是一个实例范围的操作。该闭包仅仅仅仅在给定实例发射给定信号时调用。
It is also possible to connect a different kind of callback on a given signal: emission hooks are invoked whenever a given signal is emitted whatever the instance on which it is emitted. Emission hooks are used for example to get all mouse_clicked emissions in an application to be able to emit the small mouse click sound. Emission hooks are connected with g_signal_add_emission_hook
and removed with g_signal_remove_emission_hook
.
可以在给定信号上连接一个不同类型的回调:发射钩子无论信号什么时候被发射,无论信号在哪一个实例上被发射都会被调用。发射钩子可以用来获取一个应用程序中的所有鼠标点击信号的发射来发出鼠标点击的声音。发射钩子可以使用g_signal_add_emission_hook来连接或使用g_signal_remove_emission_hook删除。
Signal emission 信号发射
Signal emission is done through the use of the g_signal_emit
family of functions.
信号发射通过g_signal_emit家族函数来完成。
1 2 3 4 | void g_signal_emitv (const GValue *instance_and_params, guint signal_id, GQuark detail, GValue *return_value); |
-
The
instance_and_params
array of GValues contains the list of input parameters to the signal. The first element of the array is the instance pointer on which to invoke the signal. The following elements of the array contain the list of parameters to the signal.
-
signal_id
identifies the signal to invoke.
-
detail
identifies the specific detail of the signal to invoke. A detail is a kind of magic token/argument which is passed around during signal emission and which is used by closures connected to the signal to filter out unwanted signal emissions. In most cases, you can safely set this value to zero. See the section called “The detail argument” for more details about this parameter.
-
return_value
holds the return value of the last closure invoked during emission if no accumulator was specified. If an accumulator was specified during signal creation, this accumulator is used to calculate the return value as a function of the return values of all the closures invoked during emission. If no closure is invoked during emission, thereturn_value
is nonetheless initialized to zero/null.
Signal emission can be decomposed in 5 steps:
信号的发射可以被分解为5步:
-
RUN_FIRST
: if theG_SIGNAL_RUN_FIRST
flag was used during signal registration and if there exists a class closure for this signal, the class closure is invoked.
如果在信号注册的时候设置了G_SIGNAL_RUN_FIRST并且提供了类闭包,则类闭包在这里调用。 -
EMISSION_HOOK
: if any emission hook was added to the signal, they are invoked from first to last added. Accumulate return values.
如果任何发射钩子被添加到这个信号,它们将根据被添加的顺序在这里被调用。累计返回值。 -
HANDLER_RUN_FIRST
: if any closure were connected with theg_signal_connect
family of functions, and if they are not blocked (with theg_signal_handler_block
family of functions) they are run here, from first to last connected.
如果闭包被使用g_signal_connect家族函数连接,并且它们没有阻塞(通过g_signal_handler_block家族函数),这些闭包将按连接的顺序在这里运行。 -
RUN_LAST
: if theG_SIGNAL_RUN_LAST
flag was set during registration and if a class closure was set, it is invoked here.
如果在信号注册的时候设置了G_SIGNAL_RUN_LAST并且提供了类闭包,则类闭包在这里调用。 -
HANDLER_RUN_LAST
: if any closure were connected with theg_signal_connect_after
family of functions, if they were not invoked duringHANDLER_RUN_FIRST
and if they are not blocked, they are run here, from first to last connected.
如果闭包被使用g_signal_connect_after家族函数连接,并且它们没有阻塞(通过g_signal_handler_block家族函数),也没有在HANDLER_RUN_FIRST阶段被调用,这些闭包将按连接的顺序在这里运行。 -
RUN_CLEANUP
: if theG_SIGNAL_RUN_CLEANUP
flag was set during registration and if a class closure was set, it is invoked here. Signal emission is completed here.
如果在信号注册的时候设置了G_SIGNAL_RUN_CLEANUP并且提供了类闭包,则类闭包在这里调用。信号的发射在这里完成。
If, at any point during emission (except in RUN_CLEANUP
or EMISSION_HOOK
state), one of the closures stops the signal emission withg_signal_stop_emission
, emission jumps to RUN_CLEANUP
state.
如果在除了RUN_CLEANUP
和 EMISSION_HOOK之外的其他阶段,有一个闭包使用了g_signal_stop_emission停止信号的发射,发射将会跳到RUN_CLEANUP阶段。
If, at any point during emission, one of the closures or emission hook emits the same signal on the same instance, emission is restarted from the RUN_FIRST
state.
如果在发射的任意阶段,有一个闭包或者发射钩子在同一个实例上发射同一个信号,发射将会从RUN_FIRST阶段重新开始。
The accumulator function is invoked in all states, after invocation of each closure (except in RUN_EMISSION_HOOK
and RUN_CLEANUP
). It accumulates the closure return value into the signal return value and returns TRUE or FALSE. If, at any point, it does not return TRUE, emission jumps to RUN_CLEANUP
state.
accumulator函数在除了RUN_EMISSION_HOOK
和 RUN_CLEANUP阶段之外的任何阶段调用闭包之后被调用。它累计闭包的返回值到信号的返回值,并返回TRUE或FALSE,如果它返回了FALSE,发射将跳到RUN_CLEANUP阶段发射。
If no accumulator function was provided, the value returned by the last handler run will be returned by g_signal_emit
.
如果提供了累计函数,最后一个闭包的返回值将由g_signal_emit返回。
The detail argument 细节参数
All the functions related to signal emission or signal connection have a parameter named the detail. Sometimes, this parameter is hidden by the API but it is always there, in one form or another.
Of the three main connection functions, only one has an explicit detail parameter as a GQuark:g_signal_connect_closure_by_id
. [6]
所有与信号发射或信号连接的函数都有一个叫做detail的参数,有时候该参数被API藏在底下但它总是以某种形式存在着。在3个主要的连接函数中,detail参数只在g_signal_connect_closure_by_id
.中以GQuark的形式出现。
The two other functions, g_signal_connect_closure
and g_signal_connect_data
hide the detail parameter in the signal name identification. Their detailed_signal
parameter is a string which identifies the name of the signal to connect to. The format of this string should match signal_name::detail_name. For example, connecting to the signal named notify::cursor_position will actually connect to the signal named notify with the cursor_position detail. Internally, the detail string is transformed to a GQuark if it is present.
另外两个函数g_signal_connect_closure
和 g_signal_connect_data
把detail参数隐藏在它们的信号名称参数中。它们的detailed_signal是一个标识了信号名的字符串,该字符串的格式应该是"signal_name::detail_name"。例如,连接一个命名为 notify::cursor_position的信号事实上连接到了一个名为notify、detail为cursor_position的信号。在内部,这个detail字符串将被转换为它代表的GQuark。
Of the four main signal emission functions, one hides it in its signal name parameter: g_signal_connect
. The other three have an explicit detail parameter as a GQuark again: g_signal_emit
, g_signal_emitv
and g_signal_emit_valist
.
四个主要的信号发射函数中, g_signal_connect
把detail参数隐藏在信号名中,而其他的两个函数g_signal_emit
、g_signal_emitv
和 g_signal_emit_valist
提供了一个作为GQuark的detail参数。
If a detail is provided by the user to the emission function, it is used during emission to match against the closures which also provide a detail. If a closure's detail does not match the detail provided by the user, it will not be invoked (even though it is connected to a signal which is being emitted).
如果信号的发射函数有提供了一个detail,在信号发射的时候会检查信号发射的detail与闭包的detail是否匹配,如果闭包的detail与信号发射时提供的detail并不匹配,即使闭包连接到了这个信号它也不会被调用。
This completely optional filtering mechanism is mainly used as an optimization for signals which are often emitted for many different reasons: the clients can filter out which events they are interested in before the closure's marshalling code runs. For example, this is used extensively by the notify
signal of GObject: whenever a property is modified on a GObject, instead of just emitting the notifysignal, GObject associates as a detail to this signal emission the name of the property modified. This allows clients who wish to be notified of changes to only one property to filter most events before receiving them.
这个完全可选的过滤策略是对很多时候一个信号可能因为很多原因被发射的优化:用户可以在闭包的代码被调用之前过滤掉它们不感兴趣的事件。例如这个特性被GObject的notify信号广泛地信号:无论什么时候有一个GObject的属性被修改时,除了发射一个notify信号,GObject把被修改的属性名作为该信号的detail连同信号一起发射。这允许仅仅关注某个属性修改情况的用户在接受到信号前可以过滤大多数事件。
As a simple rule, users can and should set the detail parameter to zero: this will disable completely this optional filtering for that signal.
作为一个简单的规则,用户可以把detail设置为0,这会完全停止这个信号的detail过滤机制。
[6] A GQuark is an integer which uniquely represents a string. It is possible to transform back and forth between the integer and string representations with the functions g_quark_from_string
and g_quark_to_string
.
注6 GQuark是一个唯一代表一个字符串的整数。可以用函数g_quark_from_string
和 g_quark_to_string在该整数和字符串之间切换。