在前几篇文章中,从一个较高的层次上说明了SWIG的基本用法。这篇文章会深入到SWIG的一些实现的细节中。SWIG默认的封装方式,是把C/C++的struct和class封装到Python Proxy Class中。不过从SWIG 2.0.4开始, swig提供了一个新的参数-builtin, 这种封装的方式比Python Proxy Class更高效。
Python Proxy Class
Python Proxy Class是SWIG封装的一个很关键的部分。Python Proxy Class提供了一个很自然的访问C/C++代码的方式,从而能提供很多SWIG的特性。假如你有这样一段C++的代码:
class Foo {
public:
int x;
int spam(int);
...
SWIG会把这个类的成员变量和成员函数,封装成一些简单的函数。例如在foo_wrap.cxx中会有一个_wrap_new_Foo这个函数是对于new Foo的封装。在这个函数中,先会动态分配一个Foo对象,然后将这个对象包裹在SwigPyObject对象内,最后将这个SwigPyObject的对象返回。
/*
Foo *new_Foo() {
return new Foo();
}
*/
SWIGINTERN PyObject *_wrap_new_Foo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
Foo *result = 0 ;
if (!PyArg_ParseTuple(args,(char *)":new_Foo")) SWIG_fail;
result = (Foo *)new Foo();
resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Foo, SWIG_POINTER_NEW | 0 );
return resultobj;
fail:
return NULL;
}
在生成的python接口中,在python proxy class Foo 内部其实就是调用wrap_new_foo这个函数来生成一个新的SwigPyObject对象,并且把原生的Foo对象的指针包裹在自己的内部:
class Foo(_object):
def __init__(self):
this = _foo.new_Foo()
try: self.this.append(this)
except: self.this = this
Builtin type
从Python Proxy Class的细节中,我们可以知道,一个Python对象调用原生的C++对象,实际上是3层封装:
1 Python对象包含SwigPyObject
2 SwigPyObject包含原生的C++的Foo对象
3 原生的C++的Foo对象
层次越多效率越低,为此在swig 2.0.4之后推出了一个Buitin type的特性。利用这个特性实现的Python类,可以越过SwigPyObject这一层,直接包含原生的C++的Foo对象。通过减少封装的层次,提高代码的执行效率。
我们可以采用swig -builtin这个参数来激活这个特性。
通过swig -builtin参数生成的 wrap源代码中有这样的定义:
SWIGINTERN PyMethodDef SwigPyBuiltin__Foo_methods[] = {
{ "bar", (PyCFunction) _wrap_Foo_bar, METH_VARARGS, (char*) "" },
{ NULL, NULL, 0, NULL } /* Sentinel */
};
static PyHeapTypeObject SwigPyBuiltin__Foo_type = {
/*
省略成员的定义
*/
}
定义了一种新的python builtin类型 Foo_type,在python中,可以直接调用Foo_type来操纵Foo*。
不过使用Builtin 特性有一些要注意的点:
1 python 2.3之前的版本不支持
2 不能直接调用函数接口,比如new_foo这样的函数,而是直接调用Foo()
3 对象的静态成员不需要用 .cvar.成员 , 而是直接用 类名.成员
本文的代码可通过下面的链接下载:
https://dl.dropbox.com/u/35106490/swig6.tgz