GObject 参考手册:概念:背景

GObject, and its lower-level type system, GType, are used by GTK+ and most GNOME libraries to provide:
GTK+ 和大多数 GNOME 库因为使用了 GObject 和比它更低一级的类型系统── GType,从而具有下面的特性:

  • object-oriented C-based APIs and
  • 面向对象的基于 C 的 API。
  • automatic transparent API bindings to other compiled or interpreted languages.
  • 封装成其他编译型语言或动态解释语言的能力。


A lot of programmers are used to working with compiled-only or dynamically interpreted-only languages and do not understand the challenges associated with cross-language interoperability. This introduction tries to provide an insight into these challenges and briefly describes the solution chosen by GLib.
大多数程序员习惯于只使用编译型语言,或者只使用动态解析型语言,却不了解这些语言之间的关联。本文试图向你解释这些疑问,简洁地说明由 GLib 提供的解决办法。

The following chapters go into greater detail into how GType and GObject work and how you can use them as a C programmer. It is useful to keep in mind that allowing access to C objects from other interpreted languages was one of the major design goals: this can often explain the sometimes rather convoluted APIs and features present in this library.
下面的章节将带你进入一个深层次的理解:关于 GType 和 GObject 是如何工作的,作为一个C开发者你将如何使用它们。

Data types and programming
数据类型和编程

One could say (I have seen such definitions used in some textbooks on programming language theory) that a programming language is merely a way to create data types and manipulate them. Most languages provide a number of language-native types and a few primitives to create more complex types based on these primitive types.
关于程序语言的一种说法是:程序语言就是一种创建数据类型并使用函数去操作它们的语言。大多数语言提供了一系例基本类型和一些用于创建更复杂类型的原始类型。

In C, the language provides types such as char, long, pointer. During compilation of C code, the compiler maps these language types to the compiler’s target architecture machine types. If you are using a C interpreter (I have never seen one myself but it is possible :), the interpreter (the program which interprets the source code and executes it) maps the language types to the machine types of the target machine at runtime, during the program execution (or just before execution if it uses a Just In Time compiler engine).
在 C 语言中,它提供了一些基本类型如 char, long, pointer 等。在编译 C 代码的过程中,编译器将这些类型映射至编译器的目标机器的机器类型。如果你正在使用一个C解释器(虽然我从未看到过,不过理论上是有可能的),解释器(用于解释 C 代码并执行它的程序)在程序运行时,映射这些类型至目标机器的机器类型。

Perl and Python are interpreted languages which do not really provide type definitions similar to those used by C. Perl and Python programmers manipulate variables and the type of the variables is decided only upon the first assignment or upon the first use which forces a type on the variable. The interpreter also often provides a lot of automatic conversions from one type to the other. For example, in Perl, a variable which holds an integer can be automatically converted to a string given the required context:
Perl 和 Python 就是那种并不提供类似于 C 语言的类型的解释型语言。Perl 和 Python 的开发者在操作变量时,变量的类型仅在第一次分配或在第一次使用时被强制为一个类型。解释器也提供一些自动转换的变量类型的方法。例如,在 Perl 中,一个具有整型值的变量可以在需要时被自动转换成字符串:

my $tmp = 10;
print "this is an integer converted to a string:" . $tmp . "\n";

Of course, it is also often possible to explicitly specify conversions when the default conversions provided by the language are not intuitive.
当然,在编码中同样允许明确使用一个由语言本身提供的类型转换句式。

Exporting a C API
导出一个C的API

C APIs are defined by a set of functions and global variables which are usually exported from a binary. C functions have an arbitrary number of arguments and one return value. Each function is thus uniquely identified by the function name and the set of C types which describe the function arguments and return value. The global variables exported by the API are similarly identified by their name and their type.
C 的 API 是常常是一些从二进制文件中导出的函数集和全局变量。C 的函数可以有任意数量的参数和一个返回值。每个函数有唯一的由函数名确定的标识符,并且由 C 类型来描述参数和返回值。类似的,由 API 导出的全局变量也是由它们的名字和类型所标识。

A C API is thus merely defined by a set of names to which a set of types are associated. If you know the function calling convention and the mapping of the C types to the machine types used by the platform you are on, you can resolve the name of each function to find where the code associated to this function is located in memory, and then construct a valid argument list for the function. Finally, all you have to do is trigger a call to the target C function with the argument list.
一个 C 的 API 可能仅仅定义了一些类型集的关联。如果你了解函数调用和 C 类型至你所在平台的机器类型的映射关系,你可以在内存中解析到每个函数的名字从而找到这些代码所关联的函数的位置,并且构造出一个用在这个函数上的参数列表。最后,你可以用这个参数列表来呼叫这个目标 C 函数。

For the sake of discussion, here is a sample C function and the associated 32 bit x86 assembly code generated by GCC on my Linux box:
为了更好的讨论,请看下面这些 C 函数的例子和与其相关联在32位 x86 机器上由 gcc 产生的汇编代码:

static void function_foo (int foo)
{}
 
int main (int argc, char *argv[])
{
 
        function_foo (10);
 
        return 0;
}
push   $0xa
call   0x80482f4 <function_foo>

The assembly code shown above is pretty straightforward: the first instruction pushes the hexadecimal value 0xa (decimal value 10) as a 32-bit integer on the stack and calls function_foo. As you can see, C function calls are implemented by gcc by native function calls (this is probably the fastest implementation possible).
函数下显示的汇编代码是非常直观的: 第一个指令在堆栈上建立了十六进制的值 0xa(十进制为10)作为一个32位的整型,并调用了 function_foo 函数。就如你看到的,C 函数的调用由 gcc 实现成了本地机器码的调用(这是实现起来最快的方法)。

Now, let’s say we want to call the C function function_foo from a Python program. To do this, the Python interpreter needs to:
现在,让我来告诉你,一个 Python 程序是如何调用这个 C 函数 function_foo 的。为了完成调用,Python 解释器需要做:

  • Find where the function is located. This probably means finding the binary generated by the C compiler which exports this function.
  • 找到函数所处的位置:这个意味着在 C 编译器编译成的二进制文件中寻找这个函数。
  • Load the code of the function in executable memory.
  • 在可执行的内存中,载入有关这个函数的相关代码。
  • Convert the Python parameters to C-compatible parameters before calling the function.
  • 在调用这个函数前,将Python的参数转换为 C 兼容的参数。
  • Call the function with the right calling convention.
  • 用正确的方式调用这个函数。
  • Convert the return values of the C function to Python-compatible variables to return them to the Python code.
  • 将 C 函数的返回值转换成 Python 兼容的变量并将其返回至 Python 代码中。

The process described above is pretty complex and there are a lot of ways to make it entirely automatic and transparent to C and Python programmers:
上面所描述的处理过程是相当复杂的。不过有几个方法可以使它完全自动化,并且使得其过程对 C 和 Python 开发者而言都是透明的:

  • The first solution is to write by hand a lot of glue code, once for each function exported or imported, which does the Python-to-C parameter conversion and the C-to-Python return value conversion. This glue code is then linked with the interpreter which allows Python programs to call Python functions which delegate work to C functions.
    第一个解决办法是手动编写一些“粘合代码”,当每个函数被导入或导出时,使用这些代码将 Python 的参数转换为 C 兼容的参数,并将 C 的返回值转换为 Python 兼容的返回值。这个粘合代码将被连接到解释器上,从而解释器在解释 Python 程序时,可以完成程序中的调用 C 函数的工作。

  • Another, nicer solution is to automatically generate the glue code, once for each function exported or imported, with a special compiler which reads the original function signature.
    另外一个更好的解决办法是自动产生粘合代码,当每个函数被导入或导出时,使用一个特殊的编译器来读取原始的函数签名。

  • The solution used by GLib is to use the GType library which holds at runtime a description of all the objects manipulated by the programmer. This so-called dynamic type library is then used by special generic glue code to automatically convert function parameters and function calling conventions between different runtime domains.
    GLib 用的解决办法是,使用 GType 库来保存在当前运行环境中的所有由开发者描述的对象的描述。这些“动态类型”库将被特殊的“通用粘合代码”来自动转换函数参数和进行函数调用在不同的运行环境之间。

The greatest advantage of the solution implemented by GType is that the glue code sitting at the runtime domain boundaries is written once: the figure below states this more clearly.
这个由 GType 实现的解决方法有很大的优势,在运行环境边界之间的粘合代码只写一次就够了。下图或许会说明的更加清楚:

Currently, there exist at least Python and Perl generic glue code which makes it possible to use C objects written with GType directly in Python or Perl, with a minimum amount of work: there is no need to generate huge amounts of glue code either automatically or by hand.
当前,至少存在于 Python 和 Perl 的通用粘合代码使得直接在 Python 或 Perl 使用用 GType 写的 C 对象成为可能,只要最少的工作量即可:没有必要去自动或手动去生成巨大的粘合代码。

Although that goal was arguably laudable, its pursuit has had a major influence on the whole GType/GObject library. C programmers are likely to be puzzled at the complexity of the features exposed in the following chapters if they forget that the GType/GObject library was not only designed to offer OO-like features to C programmers but also transparent cross-language interoperability.
这个目标被论证是值得称赞的,对它的追求影响了整个 GType/GObject 库。不过,C 开发者很可能会被接下来几章所揭露的特性的复杂性所被难住,如果他们忘了 GType/GObject 库不仅仅是为了设计向 C 开发者提供面向对象的特性,也是为了透明的跨语言互通性。

Leave a Reply

Your email address will not be published. Required fields are marked *