GObject对象系统(6) GObject消息系统



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

    * 一个在调用闭包时传递给回调的user_data指针
 
  • 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.

    * 一个代表这个闭包的析构函数的函数指针:当闭包的引用计数减到0时,在闭包结构被摧毁之前这个函数将被调用
 

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) and g_closure_add_marshal_guards (invocation notification). There exist symmetric deregistration functions for finalization and invalidation events (g_closure_remove_finalize_notifier and g_closure_remove_invalidate_notifier) but not for the invocation process. [5]

一个GClosure提供了如下简单的服务:
* 调用(g_closure_invoke):这就是闭包被创造出来的原因:对调用者隐藏调用回调的细节;
* 通知:闭包可以通知监听者例如闭包调用、闭包失效、闭包析构等确定的事件。监听函数可以通过 g_closure_add_finalize_notifier(析构通知)、 g_closure_add_invalidate_notifier(失效通知)和 g_closure_add_marshal_guards(调用通知)来注册。存在对称的用于解除注册析构和失效监听器的函数( 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_newvg_signal_new_valist or g_signal_newfunctions:

要在一个已存在的类型上注册一个信号,我们可以使用g_signal_newvg_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 ignore NULL 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.

                传给accumeulator的指针
  • c_marshaller: this is the default C marshaller for any closure which is connected to this signal.

                连接到这个函数的所有闭包的默认C调用器
  • 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_override_class_closure来重写给定类型的类闭包。仅仅在某个类的派生类中才可以使用这个函数来重写该类型信号的类闭包。该函数仅仅被用于语言绑定。
* 你可以使用  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, the return_value is nonetheless initialized to zero/null.

                如果没有accumulator被设置的化用来存储闭包调用的最后一个返回值。

Signal emission can be decomposed in 5 steps:

信号的发射可以被分解为5步:

  1. RUN_FIRST: if the G_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并且提供了类闭包,则类闭包在这里调用。

  2. EMISSION_HOOK: if any emission hook was added to the signal, they are invoked from first to last added. Accumulate return values.
                    如果任何发射钩子被添加到这个信号,它们将根据被添加的顺序在这里被调用。累计返回值。

  3. HANDLER_RUN_FIRST: if any closure were connected with the g_signal_connect family of functions, and if they are not blocked (with the g_signal_handler_block family of functions) they are run here, from first to last connected.
                    如果闭包被使用g_signal_connect家族函数连接,并且它们没有阻塞(通过g_signal_handler_block家族函数),这些闭包将按连接的顺序在这里运行。

  4. RUN_LAST: if the G_SIGNAL_RUN_LAST flag was set during registration and if a class closure was set, it is invoked here.
                    如果在信号注册的时候设置了G_SIGNAL_RUN_LAST并且提供了类闭包,则类闭包在这里调用。

  5. HANDLER_RUN_LAST: if any closure were connected with the g_signal_connect_after family of functions, if they were not invoked during HANDLER_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阶段被调用,这些闭包将按连接的顺序在这里运行。

  6. RUN_CLEANUP: if the G_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_emitg_signal_emitv and g_signal_emit_valist.

四个主要的信号发射函数中, g_signal_connect把detail参数隐藏在信号名中,而其他的两个函数g_signal_emitg_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在该整数和字符串之间切换。

 

 

 

 

转载于:https://my.oschina.net/wsgalaxy/blog/3008511

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值