对swig对C/C++函数的封装原理进行了初步的学习(内容很少,主要是避免让我写代码的时候陷入强迫症的苦恼。
看到KMCLib中在python中直接使用c++的函数,不禁又让我开始联想这是为什么,于是我就自己写个小测试看看swig是怎么封装函数的。
先定义两个C++函数:
1 2 3 4 5
|
int add(int a, int b);
double showg();
|
函数实现文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#include "temp.h"
int add(int a, int b) { return a + b; }
static double g = 10.0;
double showg() { return g; }
|
开始用swig进行封装:
1 2 3
| $ swig -python -c++ -verbose temp.i
$ g++ -shared -fPIC temp.cpp temp_wrap.cxx -o _temp.so
|
ok,这个程序能在python中执行,我现在想看swig是怎么封装这两个函数的。
先看看python直接调用的py模块文件中的代码,找到add和showg函数的定义:
1 2 3 4 5 6 7
| def add(a, b): return _temp.add(a, b) add = _temp.add
def showg(): return _temp.showg() showg = _temp.showg
|
可见他们都是将_temp模块中的相应的函数进行了封装或者说代理。那么python调用的应该是封装过以后的c++函数,毕竟python不可能直接调用C++函数(不然为什么要封装。。。
在temp_wrap.cpp中可以找到swig对两个原始C++函数的封装函数,通过封装,能够将传入的python数据转换成C++的数据类型并调用真正的C++函数返回C++数据,在将C++数据转换成python相应的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| SWIGINTERN PyObject *_wrap_add(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; int arg1 ; int arg2 ; int val1 ; int ecode1 = 0 ; int val2 ; int ecode2 = 0 ; PyObject * obj0 = 0 ; PyObject * obj1 = 0 ; int result; if (!PyArg_ParseTuple(args,(char *)"OO:add",&obj0,&obj1)) SWIG_fail; ecode1 = SWIG_AsVal_int(obj0, &val1); if (!SWIG_IsOK(ecode1)) { SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "add" "', argument " "1"" of type '" "int""'"); } arg1 = static_cast< int >(val1); ecode2 = SWIG_AsVal_int(obj1, &val2); if (!SWIG_IsOK(ecode2)) { SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "add" "', argument " "2"" of type '" "int""'"); } arg2 = static_cast< int >(val2); result = (int)add(arg1,arg2); resultobj = SWIG_From_int(static_cast< int >(result)); return resultobj; fail: return NULL; }
|
由于在python中所有的数据包括int
, float
这种基本数据类型也都是用PyObject
来实现的,因此swig要做的数据转换就是PyObject
<–> int
等数据之间的转换,数据转换好以后就可以在封装函数内直接调用C++的函数了。我特地做了个图:

可见所谓函数封装也就是给真正的内部函数套一个壳子,壳子的主要作用是数据转换工作(传入参数和返回值的操作)。这样我们在python中调用C++函数实际上就是操作壳子中的C/C++函数。