::-- JinQing [2007-04-03 07:38:41]

22.6. The SWIG Integration Code Generator

22.6. 集成代码生成器SWIG

But don't do that. As you can probably tell, manual coding of C extensions can become fairly involved (this is almost inevitable in C language work). I've introduced the basics in this chapter thus far so that you understand the underlying structure. But today, C extensions are usually better and more easily implemented with a tool that generates all the required integration glue code automatically. There are a variety of such tools for use in the Python world, including SIP, SWIG, and Boost.Python; we'll explore alternatives at the end of this chapter. Of these, the SWIG system is likely still the most widely used.

但是,我们不必手工编码。正如你所知,手工编写C扩展是相当复杂的(C语言的活儿就是这样)。目前为止,本章介绍的内容只是基础知识,用来帮助你理解底层的构造。如今,C扩展通常使用工具来自动生成所有的集成粘合代码,比手工实现更好更轻松。这样的工具有许多,如SIP, SWIG, 及Boost.Python;我们会在本章末尾探索各种不同的工具。而其中,SWIG可能是使用最广泛的工具。

The Simplified Wrapper and Interface Generator (SWIG) is an open source system created by Dave Beazley and now developed by its community, much like Python. It uses C and C++ type declarations to generate complete C extension modules that integrate existing libraries for use in Python scripts. The generated C (and C++) extension modules are complete: they automatically handle data conversion, error protocols, reference-count management, and more.

SWIG(简单封装和接口生成器)是一个开放源代码系统,由Dave Beazley创建,现由其社群开发,很像Python. 它利用C和C++类型声明来生成完整的C扩展模块,生成的C扩展模块可以集成到现有库中供Python脚本使用。生成的C(和C++)扩展模块是完整的:它们自动处理数据转换,错误处理,引用计数,等等。

That is, SWIG is a program that automatically generates all the glue code needed to plug C and C++ components into Python programs; simply run SWIG, compile its output, and your extension work is done. You still have to manage compilation and linking details, but the rest of the C extension task is largely performed by SWIG.

就是说,SWIG是一个程序,它能自动生成所有粘合代码,将C和C++部件插入到Python程序中;只需运行SWIG,编译其输出代码,你的扩展模块就完成了。你仍须编译和链接,但是C扩展的其它大部份工作都可由SWIG完成。

22.6.1. A Simple SWIG Example

22.6.1. 一个SWIG简例

To use SIWG, instead of writing all that C code in the prior sections, write the C function you want to use from Python without any Python integration logic at all, as though it is to be used from C alone. For instance, Example 22-5 is a recoding of Example 22-1 as a straight C function.

使用SWIG,你不必有任何Python集成的逻辑,不必编写前面几节那样的代码,只需编写你的C函数,就像它是仅用于C语言的。例如,例22-5是对例22-1的重写,是一个纯粹的C函数。

Example 22-5. PP3E\Integrate\Extend\HelloLib\hellolib.c

/*********************************************************************
 * A simple C library file, with a single function, "message",
 * which is to be made available for use in Python programs.
 * There is nothing about Python here--this C function can be
 * called from a C program, as well as Python (with glue code).
 *
 * 一个简单的C语言库文件,只有一个函数,"message", 
 * 将会成为可被Python程序使用的函数。
 * 这里没有任何Python相关的东西——这个C函数可以
 * 由C程序调用,也可以由Python程序调用(加上粘合代码后)。
 *********************************************************************/

#include <string.h>
#include <hellolib.h>

static char result[64];                  /* this isn't exported */

char *
message(char *label)                     /* this is exported */
{
    strcpy(result, "Hello, ");           /* build up C string */
    strcat(result, label);               /* add passed-in label */
    return result;                       /* return a temporary */
}


While you're at it, define the usual C header file to declare the function externally, as shown in Example 22-6. This is probably overkill for such a small example, but it will prove a point.

然后,写个普通的头文件来声明外部函数,如例22-6所示。对于这样一个小例子来说,这好像是多余的,但是下面将会用到。

Example 22-6. PP3E\Integrate\Extend\HelloLib\hellolib.h

/********************************************************************
 * Define hellolib.c exports to the C namespace, not to Python
 * programs--the latter is defined by a method registration
 * table in a Python extension module's code, not by this .h;
 *
 * 定义hellolib.c输出到C语言的符号(不是输出到Python程序)。
 * 输出到Python程序的符号在Python扩展模块代码中由一个方法注册表定义,
 * 而不是在这个.h文件中。
 ********************************************************************/

extern char *message(char *label);


Now, instead of all the Python extension glue code shown in the prior sections, simply write a SWIG type declarations input file, as in Example 22-7.

现在,无需前几节所示的所有Python扩展粘合代码,只需简单地写一个SWIG类型声明输入文件,如例22-7。

Example 22-7. PP3E\Integrate\Extend\Swig\hellolib.i

/******************************************************
 * Swig module description file, for a C lib file.
 * Generate by saying "swig -python hellolib.i".
 ******************************************************/

%module hellowrap

%{
#include <hellolib.h>
%}

extern char *message(char*);    /* or: %include "../HelloLib/hellolib.h"   */
                                /* or: %include hellolib.h, and use -I arg */


This file spells out the C function's type signature. In general, SWIG scans files containing ANSI C and C++ declarations. Its input file can take the form of an interface description file (usually with a .i suffix) or a C/C++ header or source file. Interface files like this one are the most common input form; they can contain comments in C or C++ format, type declarations just like standard header files, and SWIG directives that all start with %. For example:

这个文件列出了C函数的类型签名。通常,SWIG需要扫描包含ANSI C和C++声明的文件,它的输入文件可以是一个接口描述文件(通常是.i后缀),也可以是一个C/C++头文件或源文件。接口文件是最常用的形式,就像这个,可以包含C或C++格式的注释,与标准头文件一样的类型声明,以及以%开头的SWIG指令。例如:

%module

Sets the module's name as known to Python importers.

%module 设置模块名,Python导入的就是这个名字。

%{...%}

Encloses code added to generated wrapper file verbatim.

%{...%}括起来的代码将原封不动地添加到生成的封装文件中。

extern statements

Declare exports in normal ANSI C/C++ syntax.

extern语句用标准的ANSI C/C++语法声明输出的符号。

%include

Makes SWIG scan another file (-I flags give search paths).

%include命令SWIG扫描另一个文件(可以用-I标志给出搜索路径)。

In this example, SWIG could also be made to read the hellolib.h header file of Example 22-6 directly. But one of the advantages of writing special SWIG input files like hellolib.i is that you can pick and choose which functions are wrapped and exported to Python, and you may use directives to gain more control over the generation process.

在本例中,SWIG也可以直接读取例22-6的hellolib.h头文件。但是,专门写一个像hellolib.i这样的SWIG输入文件至少有一个好处,就是你可以挑选哪些函数要封装并输出到Python,而且你还可以用指令来更好地控制代码的生成。

SWIG is a utility program that you run from your build scripts; it is not a programming language, so there is not much more to show here. Simply add a step to your makefile that runs SWIG and compile its output to be linked with Python. Example 22-8 shows one way to do it on Cygwin.

SWIG是由构建脚本调用的通用程序,它不是一种编程语言,所以也没什么好讲的。只需在make文件中添加一步运行SWIG,然后就可以编译SWIG的输出,并与Python链接。例22-8展示了在Cygwin上的做法。

Example 22-8. PP3E\Integrate\Extend\Swig\makefile.hellolib-swig

##################################################################
# Use SWIG to integrate hellolib.c for use in Python programs on
# Cygwin.  The DLL must have a leading "_" in its name in current
# SWIG (>1.3.13) because also makes a .py without "_" in its name.
#
# 在Cygwin上利用SWIG集成hellolib.c,使之为Python程序所用。
# DLL文件的名字必须有一个前导的"_",因为当前版本的SWIG(>1.3.13)
# 会用模块名生成一个.py文件。
##################################################################

PYLIB = /usr/bin
PYINC = /usr/include/python2.4
CLIB  = ../HelloLib

# the library plus its wrapper
_hellowrap.dll: hellolib_wrap.o $(CLIB)/hellolib.o
        gcc -shared hellolib_wrap.o $(CLIB)/hellolib.o \
                       -L$(PYLIB) -lpython2.4 -o $@

# generated wrapper module code
hellolib_wrap.o: hellolib_wrap.c $(CLIB)/hellolib.h
        gcc hellolib_wrap.c -g -I$(CLIB) -I$(PYINC) -c -o $@

hellolib_wrap.c: hellolib.i
        swig -python -I$(CLIB) hellolib.i

# C library code (in another directory)
$(CLIB)/hellolib.o: $(CLIB)/hellolib.c $(CLIB)/hellolib.h
        gcc $(CLIB)/hellolib.c -g -I$(CLIB) -c -o $(CLIB)/hellolib.o

clean:
        rm -f *.dll *.o *.pyc core
force:
        rm -f *.dll *.o *.pyc core hellolib_wrap.c hellowrap.py


When run on the hellolib.i input file by this makefile, SWIG generates two files:

hellolib_wrap.c

The generated C extension module glue code file.[*]

[*] You can wade through this generated file in the book's examples distribution if you are so inclined, though they are highly prone to change over time (in fact, the .py module generated by SWIG for this example is also new since the second edition of this book). Also see the file PP3E\Integrate\Extend\HelloLib\hellolib_wrapper.c in the book's examples distribution for a handcoded equivalent; it's shorter because SWIG also generates extra support code.

hellowrap.py

A Python module that imports the generated C extension module.

当用这个make文件和hellolib.i输入文件执行make,SWIG会生成两个文件:

hellolib_wrap.c hellowrap.py

hellolib_wrap.c是生成的C扩展模块粘合代码文件[*]。hellowrap.py是一个Python模块,它导入生成的C扩展模块。

[*] 如果你想,你可以啃读这个生成的文件,本书例程中包含这个文件,但是它们很有可能随时间而改变(事实上,本例中SWIG生成的.py模块在本书第二版中已经是新的了)。也可以阅读本书例程中的一个文件,PP3E\Integrate\Extend\HelloLib\hellolib_wrapper.c,这是一个功能相同但是手工编码的文件;它比较短,因为SWIG生成了额外的支持代码。

The former is named for the input file, and the later per the %module directive. Really, SWIG generates two modules today: it uses a combination of Python and C code to achieve the integration. Scripts ultimately import the generated Python module file, which internally imports the generated and compiled C module.

前者是按SWIG输入文件命名的,后者是按%module指令命名的。实际上,SWIG生成了两个模块:它利用Python和C代码的组合来达到集成。Python脚本导入生成的Python模块文件,该模块内部又导入了生成并已编译的C代码模块。

To build the C module, the makefile runs a compile after running SWIG, and then combines the result with the original C library code:

构建C模块时,make文件先运行SWIG,然后编译,再与原来的C库代码链接。

.../PP3E/Integrate/Extend/Swig$ make -f makefile.hellolib-swig force
rm -f *.dll *.o *.pyc core hellolib_wrap.c hellowrap.py

.../PP3E/Integrate/Extend/Swig$ ls
Environ  Shadow  hellolib.i  makefile.hellolib-swig

.../PP3E/Integrate/Extend/Swig$ make -f makefile.hellolib-swig
swig -python -I../HelloLib hellolib.i
gcc hellolib_wrap.c -g -I../HelloLib -I/usr/include/python2.4 -c
    -o hellolib_wrap.o
gcc -shared hellolib_wrap.o ../HelloLib/hellolib.o \
                       -L/usr/bin -lpython2.4 -o _hellowrap.dll

.../PP3E/Integrate/Extend/Swig$ ls
Environ  _hellowrap.dll  hellolib_wrap.c  hellowrap.py
Shadow   hellolib.i      hellolib_wrap.o  makefile.hellolib-swig


More specifically, the makefile runs SWIG over the input file, compiles the generated C glue code file into a .o object file, and then links it with hellolib.c's compiled object file to produce _hellowrap.dll. The result is a dynamically loaded C extension module file ready to be imported by Python code. Like all modules, _hellowrap.dll must, along with hellowrap.py, be placed in a directory on your Python module search path (a period [.] will suffice if you're working in the directory where you compile).

更具体点说,make文件以.i文件为参数运行SWIG,生成了C代码的粘合文件,再把C粘合文件编译为.o目标文件,并与hellolib.c编译的目标文件链接,产生了_hellowrap.dll。结果是一个动态装载的C扩展模块文件,能够导入到Python代码中。和所有模块一样,_hellowrap.dll和hellowrap.py必须放在Python模块搜索路径下(如果你的工作目录和编译的目录相同,用一个点号[.]就行了)。

Notice that the .dll file must be built with a leading underscore in its name; as of SWIG 1.3.14, this is required because SWIG also created the .py file of the same name without the underscore.

注意.dll文件名字开头必须带个下划线;这是SWIG 1.3.4所要求的,因为SWIG要用不带下划线的相同名字创建.py文件。

As usual in C development, you may have to barter with the makefile to get it to work on your system. Once you've run the makefile, though, you are finished. The generated C module is used exactly like the manually coded version shown before, except that SWIG has taken care of the complicated parts automatically:

和普通的C开发一样,你可能要对make文件作番手脚才能让它在你的系统上工作。然而,一旦make文件能跑起来,你就成了。生成的C模块代码用起来就像以前手工编码的那个版本一样,只是SWIG已经自动处理了那些繁琐的事情。

.../PP3E/Integrate/Extend/Swig$ python
 >>> import hellowrap                       # import glue + library file
>>> hellowrap.message('swig world')         # cwd always searched on imports
'Hello, swig world'

>>> hellowrap._ _file_ _
'hellowrap.py'
>>> dir(hellowrap)
['_ _builtins_ _', '_ _doc_ _', '_ _file_ _', '_ _name_ _', '_hellowrap', ...]


In other words, once you learn how to use SWIG, you can largely forget all the integration coding details introduced in this chapter. In fact, SWIG is so adept at generating Python glue code that it's usually much easier and less error prone to code C extensions for Python as purely C- or C++-based libraries first, and later add them to Python by running their header files through SWIG, as demonstrated here.

换句话说,一旦你学会如何使用SWIG,你大可以忘掉本章介绍的集成编码的所有繁琐细节。事实上,SWIG非常擅长Python粘合代码的生成,使用SWIG会大大简化C扩展的编写,并且不易犯错,所以通常的做法是先编写纯的C或C++库,再将它们的头文件传给SWIG,就像这里所演示的。

22.6.2. SWIG Details

22.6.2. SWIG详情

Of course, you must have SWIG before you can run SWIG; it's not part of Python itself. Unless it is already on your system, fetch SWIG off the Web and run its installer or build it from its source code. To do the latter, you'll need a C++ compiler; see SWIG's README file and web site for more details. SWIG is a command-line program and generally can be run just by saying the following:

当然,你必须有SWIG才能运行;SWIG并不是Python自带的。如果没有,就从网上下载安装或从源代码编译。编译需要一个C++编译器,详情请阅SWIG README文件和网站。SWIG是命令行程序,通常这样运行:

swig -python hellolib.i


Along the way in this chapter, we'll meet a few more SWIG-based alternatives to the remaining examples. By way of introduction, here is a quick look at a few more SWIG highlights:

在本章后面的例子中,我们还会使用到SWIG的其它功能,主要是:

C++ "shadow" classes

C++ “影子”类

We'll learn how to use SWIG to integrate C++ classes for use in your Python scripts. When given C++ class declarations, SWIG generates glue code that makes C++ classes look just like Python classes in Python scripts. In fact, C++ classes are Python classes under SWIG; you get what SWIG calls a C++ shadow (or proxy) class that interfaces with a C++-coded extension module, which in turn talks to C++ classes using a function-based interface. Because the integration's outer layer is Python classes, those classes may be subclassed in Python and their instances processed with normal Python object syntax.

我们将学习如何使用SWIG来集成C++类,使之用于Python脚本。只要提供C++类声明,SWIG就能生成粘合代码,使得C++类就像Python类一样在Python脚本中使用。事实上,C++类就是SWIG中的Python类;你会得到一个C++扩展模块类,在SWIG中称为C++影子类(或代理类),它通过函数接口操纵C++类。因为集成的外层是Python类,所以可以在Python中继承,它们的实例可以像正常的Python对象一样进行处理。

Variables

变量

Besides functions and C++ classes, SWIG can also wrap C global variables and constants for use in Python: they become attributes of an object named cvar inserted in generated modules (e.g., module.cvar.name fetches the value of C's variable name from a SWIG-generated wrapper module).

除了函数和C++类,SWIP也可以封装C全局变量和常量,使之用于Python:在生成的模块中,它们成为一个叫cvar的对象的属性(比如:module.cvar.name就是从SWIG生成模块中提取C变量name的值)。

structs

结构

C structs are converted into a set of get and set accessor functions that are called to fetch and assign fields with a struct object pointer (e.g., module.Vector_fieldx_get(v) fetches C's Vector.fieldx from a Vector pointer v, like C's v->fieldx). Similar accessor functions are generated for data members and methods of C++ classes (the C++ class is roughly a struct with extra syntax), but the SWIG shadow class feature allows you to treat wrapped classes just like Python classes, instead of calling the lower-level accessor functions.

C结构被转换成为一组set和get存取函数,可通过一个结构对象指针来进行赋值或取值(例如,module.Vector_fieldx_get(v)就是从一个Vector指针v中提取C结构成员Vector.fieldx,就像C代码v->fieldx)。C++类的数据成员和成员函数也会生成类似的存取函数(C++类也可看作为一个结构,只是具有额外的语法),但是SWIG影子类已经让你可以像Python类一样地对待封装后的类,而不必调用低层的存取函数。

Other

其它

For C++, besides wrapping up classes and functions for use from Python, SWIG also generates code to support overloaded operators, routing of virtual method calls from C++ back to Python, templates, and much more.

对C++来说,除了封装类和函数供Python使用,SWIG生成的代码也支持重载操作符,虚函数调用,模板,等等。

Consult the SWIG Python user manual for the full scoop on its features. SWIG's feature set and implementation are both prone to change over time (e.g., its pointers are no longer strings, and Python new-style classes are employed in dual-mode proxy classes), so we'll defer to its documentation for more internals information.

请查阅SWIG Python用户手册获知其完整的特性。SWIG特性表与实现都有可能随时间而变化(比如,它的指针不再是字符串,又如,在双模式代理类中采用Python的新型类),因此需要参考它的手册才能获取更多的内部信息。

Later in this chapter, we'll see SWIG in action two more times, wrapping up C environment calls and a C++ class. Although the SWIG examples in this book are simple, you should also know that SWIG handles industrial-strength libraries just as easily. For instance, Python developers have successfully used SWIG to integrate libraries as complex as Windows extensions and commonly used graphics APIs such as OpenGL.

本章后面还有两个SWIG的实际应用,一个是封装C环境变量调用,一个是封装C++类。虽然本书SWIG的例子是简单的,但你应该了解,SWIG应付工业级的函数库也是相当轻松的。例如,Python开发者们已经成功地应用SWIG集成了象Windows扩展库这样复杂的库以及象OpenGL这样通用的图形接口库。

SWIG can also generate integration code for other scripting languages such as Tcl and Perl. In fact, one of its underlying goals is to make components independent of scripting language choicesC/C++ libraries can be plugged into whatever scripting language you prefer to use (I prefer to use Python, but I might be biased). SWIG's support for things such as classes seems strongest for Python, though, probably because Python is considered to be strong in the classes department. As a language-neutral integration tool, SWIG addresses some of the same goals as systems such as COM and CORBA (described in Chapter 23), but it provides a code generation-based alternative rather than an object model.

SWIG也能为其它脚本语言如Tcl和Perl生成集成代码。实际上,SWIG的目的之一是使部件独立于脚本语言的选择。使用SWIG,C/C++库可以插入到任何一种你喜欢的脚本语言中(我喜欢用Python)。因为SWIG支持类,而Python也是在类方面被认为是强大的,所以SWIG对Python的支持好象是最强的。作为语言无关的集成工具,SWIG与COM和CORBA(见23章)的目标有许多相同之处,但是SWIG提供的是一个基于代码生成的方法,而不是一个对象模型。

You can find SWIG by a web search or by visiting its current home page on the Web at http://www.swig.org. Along with full source code, SWIG comes with outstanding documentation (including documentation specifically for Python). The documentation also describes how to build SWIG extensions with other platforms and compilers, including standard Windows without Cygwin.

你可以搜索SWIG,或者访问当前的主页http://www.swig.org来下载SWIG. 除了完整的源代码,SWIG还附带有卓越的文档(其中有针对Python的文档)。文档也描述了如何在其它平台和编译器上构建SWIG扩展,例如无Cygwin的标准Windows。