What is G-object?
Why Bother to use Gobject?
1)使用面向对象的设计方法来编程。GObject仅依赖于 GLib和 libc,通过它可使用纯C语言设计一整套面向对象的软件模块。
2)多语言交互。在为已经使用 GObject框架写好的函数库建立多语言连结时,可以很容易对应到许多语言,包括C++、Java、Ruby、Python和.NET/Mono等。GObject被设计为可以直接使用在 C 程序中,也 封装至其他语言。
透明的跨语言互通性
- static void function_foo(int foo)
- {
- }
- int main(int argc, char *argv[])
- {
- function_foo(10)
- return 0;
- }
如果你了解函数调用和C类型至你所在平台的机器类型的映射关系,你可以在内存中解析到每个函数的名字从而找到这些代码所关联的函数的位置,并且构造出一个用在这个函数上的参数列表。最后,你可以用这个参数列表来调用这个目标C函数。第一个指令在堆栈上建立了十六进制的值0xa(十进制为10)作为一个32位的整型,并调用了function_foo函数。就如你看到的,C函数的调用由gcc实现成了本地机器码的调用(这是实现起来最快的方法)。
- push $0xa
- call 0x80482f4 <function_foo>
有了gcc这个第三方,我们的代码与机器的沟通更顺畅了!记住:GType/GObject库不仅仅是为了设计向C开发者提供面向对象的特性,也是为了 透明的 跨语言互通性。
做一个受欢迎的协调者
(1)找到函数所处的位置:这个意味着在C编译器编译成的二进制文件中寻找这个函数。
(2)在可执行的内存中,载入有关这个函数的相关代码。
(3)在调用这个函数前,将Python的参数转换为C兼容的参数。
(4)用正确的方式调用这个函数。
(5)将C函数的返回值转换成Python兼容的变量并将其返回至Python代码中。
来自动转换函数参数和进行函数调用在不同的运行环境之间。
GOBJECT模拟封装
在 GObject世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。有点绕。类、对象、实例有什么区别?可以这么理解,类-对象-实例,无非就是类型,该类型所声明的变量,变量所存储的内容。后面可以知道,类结构体初始化函数一般被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。所有实例共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。
下面我们摘取一段示例代码(包含示例结构体和类结构体)来帮助更好的理解上述概念:
GOBJCT如何模拟私有属性
C语言实现CLASS域GOBJECT支持
如何实现gobject面向对象支持呢?
很简单,我们只需要建立自己的头文件,并添加一些宏定义G_DEFINE_TYPE即可。
这样,GUPnPContext就成为了Gobject库认可的一类合法公民了,即成功的把GUpnPContextClass类所代表的type(类型)注册到了glib类型系统中,并且将成功获取到一个类型ID。
也就是说,当你设计新类时,GUPnPContext可以被考虑加进你的继承体系,同时GUPnPContext也可以被用于组合成其他的类。
进一步理解GType类型系统
GOBEJCT如何实现继承
GOBJECT构造函数
多态的概念
为什么要在GOBJECT引入多态?
Gobject如何实现多态?
(1)Gobject为每个子类在内存中保存了一份包含成员函数指针的表. 这个表,就是我们在C++经常说到的虚方法表(vtable)。当你想调用一个虚方法时,你必须先向系统请求查找这个对象所对应的虚方法表。这张表包含了一个由函数指针组成的结构体。在调用这些函数时,需要在运行时查找合适的函数指针,这样就能允许子类覆盖这个方法,我们称之为“虚函数”。
(2) Gobject系统要求我们向它注册新声明的类型,系统同时要求我们去向它注册(对象的和类的)结构体构造和析构函数(以及其他的重要信息),这样系统就能正确的实例化我们的对象。
(3)Gobject系统通过枚举化所有的向它注册的类型来记录新的对象类型,并且要求所有实例化对象的第一个成员是一个指向它自己类的虚函数表的指针,每个虚函数表的第一个成员是它在系统中保存的枚举类型的数字表示。
由常用的g_object_new()想到的
GOBJECT多态:将丑陋封锁在内部
(1)实现xx_xx_set_property与xx_xx_get_property函数,完成g_object_new函数“属性名-属性值”结构向Gobject子类属性的映射;
(2)在Gobject子类的类结构体初始化函数中,让Gobject基类的两个函数指针set_property与get_property分别指向xx_xx_set_property与xx_xx_get_property。
(3)在Gobject子类的类结构体初始化函数中,为Gobject子类安装具体对象的私有属性。
可以看出,set_property是Gobject的虚函数实现,是运行时的多态。
GOBJECT多态:将优雅展示于外界
GOBJECT属性实现:泛型与多态
假设我们需要一种数据类型,可以实现一个可以容纳多类型元素的链表,我想为这个链表编写一些接口,可以不依赖于任何特定的类型,并且不需要我为每种数据类型声明一个多余的函数。这种接口必然能涵盖多种类型,我们称它为GValue(Generic Value,泛型)。
要编写一个泛型的属性设置机制,我们需要一个将其参数化的方法,以及与实例结构体中的成员变量名查重的机制。从外部上看,我们希望使用C字符串来区分属性和公有API,但是内部上来说,这样做会严重的影响效率。因此我们枚举化了属性,使用索引来标识它们。
属性规格,在Glib中被称作!GParamSpec,它保存了对象的gtype,对象的属性名称,属性枚举ID,属性默认值,边界值等,类型系统用!GParamSpec来将属性的字符串
名转换为枚举的属性ID,GParamSpec也是一个能把所有东西都粘在一起的大胶水。
gobject属性设置
Gobject消息系统:闭包
一个Closure是一个抽象的、通用表示的回调(callback)。它是一个包含三个对象的简单结构:
(1)一个函数指针(回调本身) ,原型类似于:
return_type function_callback (... , gpointeruser_data);
(2) user_data指针用来在调用Closure时传递到callback。
(3)一个函数指针,代表Closure的销毁:当Closure的引用数达到0时,这个函数将被调用来释放Closure的结构。
一个GClosure提供以下简单的服务:
调用(g_closure_invoke):这就是Closure创建的目的: 它们隐藏了回调者的回调细节。
通知:相关事件的Closure通知监听者如Closure调用,Closure无效和Clsoure终结。监听者可以用注册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,但调用过程不是。
“一眼望穿”闭包
闭包给多语言绑定带来了方便
我们从分析g_signal_new函数的使用来说明这个问题。第7个参数为GSignalMarshaller类型,它与前面体面提到的GClosureMarshal是一个东西,都是一个函数指针。
GSignalCMarshaller c_marshaller:该参数是一个GSignalCMarshall类型的函数指针,其值反映了回调函数的返回值类型和额外参数类型(所谓“额外参数”,即指除回调函数中instance和user_data以外的参数)。
例如,g_closure_marshal_VOID_VOID说明该signal的回调函数为以下的callback类型:
typedef void (*callback) (gpointer instance, gpointer user_data);
而g_closure_marshal_VOID_POINTER则说明该signal的回调函数为以下的callback类型:
typedef void (*callback) (gpointer instance,gpointer arg1,gpointer user_data);
GType return_type:该参数的值应为回调函数的返回值在GType类型系统中的ID。
guintn_params:该参数的值应为回调函数的额外参数的个数。
...: 这一系列的参数的值应为回调函数的额外参数在GType类型系统中的ID,且这一系列参数中第一个参数的值为回调函数的第一个额外参数在GType类型系统中的ID,依次类推。
可以认为,信号就是包含对可以连接到信号的闭包的描述和对连接到信号的闭包的调用顺序的规定的集合体。
事实上,它是用来翻译闭包的参数和返回值类型的,它将翻译的结果传递给闭包。之所以不直接调用callback或闭包,而在外面加了一层marshal的封装,主要是方便gobjec库与其他语言的绑定。例如,我们可以写一个pyg_closure_marshal_void_string函数,其中可以调用python语言编写的“闭包”并将其计算结果传递给Gvalue容器,然后再从Gvalue容器中提取计算结果。
Gobject消息系统:Signal机制
(1)信号注册,主要解决信号与数据类型的关联问题
(2)信号连接,主要处理信号与闭包的连接问题;
(3)信号发射, 调用callback进行处理。