15.9 用WSIG包装C代码

问题

你想让你写的 C 代码作为一个 C 扩展模块来访问,想通过使用 Swig包装生成器 来完成。

解决方案

Swig 通过解析 C 头文件并自动创建扩展代码来操作。 要使用它,你先要有一个 C 头文件。例如,我们示例的头文件如下:

/* sample.h */

#include <math.h>
extern int gcd(int, int);
extern int in_mandel(double x0, double y0, int n);
extern int divide(int a, int b, int *remainder);
extern double avg(double *a, int n);

typedef struct Point {
    double x,y;
} Point;

extern double distance(Point *p1, Point *p2);

一旦你有了这个头文件,下一步就是编写一个 Swig ”接口”文件。 按照约定,这些文件以 ”.i” 后缀并且类似下面这样:

一旦你写好了接口文件,就可以在命令行工具中调用 Swig 了:

swig 的输出就是两个文件,sample_wrap.csample.py。 后面的文件就是用户需要导入的。 而 sample_wrap.c 文件是需要被编译到名叫 _sample 的支持模块的 C 代码。 这个可以通过跟普通扩展模块一样的技术来完成。 例如,你创建了一个如下所示的 setup.py 文件:

要编译和测试,在 setup.py 上执行 python3,如下:

如果一切正常的话,你会发现你就可以很方便的使用生成的 C 扩展模块了。例如:

讨论

Swig 是 Python 历史中构建扩展模块的最古老的工具之一。 Swig 能自动化很多包装生成器的处理。

所有 Swig 接口都以类似下面这样的为开头:

这个仅仅只是声明了扩展模块的名称并指定了 C 头文件, 为了能让编译通过必须要包含这些头文件(位于 %{%} 的代码), 将它们之间复制粘贴到输出代码中,这也是你要放置所有包含文件和其他编译需要的定义的地方。

Swig 接口的底下部分是一个 C 声明列表,你需要在扩展中包含它。 这通常从头文件中被复制。在我们的例子中,我们仅仅像下面这样直接粘贴在头文件中:

有一点需要强调的是这些声明会告诉 Swig 你想要在 Python 模块中包含哪些东西。 通常你需要编辑这个声明列表或相应的修改下它。 例如,如果你不想某些声明被包含进来,你要将它从声明列表中移除掉。

使用 Swig 最复杂的地方是它能给 C 代码提供大量的自定义操作。

第一个自定义是 %extend 指令允许方法被附加到已存在的结构体和类定义上。 在例子中,这个被用来添加一个 Point 结构体的构造器方法。 它可以让你像下面这样使用这个结构体:

如果略过的话,Point 对象就必须以更加复杂的方式来被创建:

第二个自定义涉及到对 typemaps.i 库的引入和 %apply 指令, 它会指示 Swig 参数签名 int *remainder 要被当做是输出值。 这个实际上是一个模式匹配规则。 在接下来的所有声明中,任何时候只要碰上 int *remainder ,他就会被作为输出。 这个自定义方法可以让 divide() 函数返回两个值。

最后一个涉及到 %typemap 指令的自定义可能是这里展示的最高级的特性了。 一个 typemap 就是一个在输入中特定参数模式的规则。 在本节中,一个 typemap 被定义为匹配参数模式 (double *a, int n) . 在 typemap 内部是一个 C 代码片段,它告诉 Swig 怎样将一个 Python 对象转换为相应的 C 参数。 本节代码使用了 Python 的缓存协议去匹配任何看上去类似双精度数组的输入参数 (比如 NumPy 数组、array 模块创建的数组等),更多请参考 15.3 小节。

在 typemap 代码内部,$1$2 这样的变量替换会获取 typemap 模式的 C 参数值 (比如 $1 映射为 double *a )。$input 指向一个作为输入的 PyObject * 参数, 而 $argnum 就代表参数的个数。

编写和理解 typemaps 是使用 Swig 最基本的前提。 不仅是说代码更神秘,而且你需要理解 Python C API 和 Swig 和它交互的方式。 Swig 文档有更多这方面的细节,可以参考下。

不过,如果你有大量的 C 代码需要被暴露为扩展模块。 Swig 是一个非常强大的工具。关键点在于 Swig 是一个处理 C 声明的编译器, 通过强大的模式匹配和自定义组件,可以让你更改声明指定和类型处理方式。 更多信息请去查阅 Swig网站 , 还有 特定于Python的相关文档

Last updated

Was this helpful?