使用SWIG 扩展Python 不指定

matrix , 2009/04/11 16:58 , 我的文档 » Python , 评论(0) , 阅读(274) , Via 本站原创 | |
Python 可以用C/C++来写扩展,在已经有C实现代码的时候,用SWIG来自动转换将更加方便。

何为SWIG,SWIG 是Simplified Wrapper and Interface Generator 的缩写详细的信息可以参考 http://www.swig.org

看一个简单例子有如下calc.h 和calc.c,一共实现了3个加法函数。我们现在要用将这三个函数实现为Python 的扩展。

calc.h
#ifndef __CALC_H
#define __CALC_H

typedef struct {
    int j;
    int k;   
} obj;

int add1(int a, int b);
void add2(int a, int b, int *c);
void add3(obj a, obj *b, obj *c);
#endif

calc.c:
#include "calc.h"

int add1(int a, int b) {
    return a + b;
}

void add2(int a, int b, int *c) {
    *c = a + b;
}

void add3(obj a, obj *b, obj *c) {
    c->j = a.j + b->j;
    c->k = a.k + b->k;
}


SWIG 需要编写一个.i为扩展名的接口文件。暂其叫calc.i 吧,内容如下:
%module calctest

%{
#include "calc.h"
%}

%include "calc.h"


正常流程,我们需要使用swig命令来生成一个calc_wrap.c文件,然后使用编译环境来啊编译Python的模块。
但是Python的Distribution模块已经可以继承编译工作和swig命令了,非常方便,编写一个setup.py,如下:
from distutils.core import setup, Extension
from os import environ

# swig's path add to current PATH.
environ["PATH"] = environ["PATH"] + ";C:\Working\swigtest\swigwin-1.3.39"

# extension for python.
calc_module = Extension(
    "_calctest",
    sources = ["calc.i", "calc.c"],
    swig_opts = [],
    )


setup(
    name="Calc",
    version = "0.1.0",
    description = "Calc test module",
    author = "Matrix Ji",
    author_email = "webmatrix.ji@gmail.com",
    ext_modules = [calc_module],
    py_modules = ["calctest"],
    )


Extension执行的时候会自动调用swig来生成calc_wrap.c,然后在进行编译。安装上去进行测试吧:
python setup.py install
安装后site-packages目录下,就有了calctest.py 和 _calctest.pyd 了。calctest.pyc 是calctest.py 预编译的。

测试如下:
>>> from calctest import add1
>>> add1(1,2)
3
>>>


接着,问题来了,add2有参数要传出啊,尽管Python的参数再传递的时候也是按照对象的指针在传递但是确不知道怎么写这个代码了。有办法了将传出参数作为返回值返回。需要在SWIG的接口文件里面进行类型映射了。可以直接使用SWIG自带的模板进行应用就可以了,修改calc.i 如下:

%module calctest

%{
#include "calc.h"
%}

%apply int *OUTPUT {int *c}

%include "calc.h"


int *OUTPUT是一个内嵌的模板,用来将int *的参数设置为传出,这样就为在执行的返回值中添加这个int型的返回值。是以添加列表的方式返回的。我们可以注意在calc_wrap.c里面生成的代码,如下:
if (SWIG_IsTmpObj(res3)) {
    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
} else {
    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
}

编译后执行的结果如下:
>>> from calctest import add1, add2, add3
>>> add2(1,2)
3
>>>

>>>

int *OUTPUT 可以有模板应用,可是obj是我们自己定义的结构体,我们如何保证add3的接口的转换很友好呢?其实由于SWIG的强大,SWIG已经自动将obj实现为一个class,因此可以直接使用了,测试代码如下:
from calctest import add3, obj

obj1 = obj()
obj2 = obj()
obj3 = obj()

obj1.j = 100;
obj1.k = 200;

obj2.j = 10;
obj2.k = 20;

print "j:%d, k:%d" % (obj3.j, obj3.k)
add3(obj1, obj2, obj3)
print "j:%d, k:%d" % (obj3.j, obj3.k)

结果输出:
j:0, k:0
j:110, k:220
Tags: ,
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]