SWIG Python简介
发布者:
Echo
SWIG Python简介
1. SWIG 是什么
SWIG是一种让脚本语言调用C/C++接口的工具,支持Perl, Python, Ruby等很多脚本。并且对C++的继承、多态、模板,STL等都有较好的支持。使用SWIG,可以实现:
1) 粘合C/C++库与脚本,快速开发
2) 交互式Debug
2. 第一个例子
假设有如下C/C++程序:
// example.h
extern double g_var;
int add(int a, int b);
class Foo
{
public:
Foo();
int GetNum() const;
void SetNum(int num);
private:
int m_num;
};
// example.cpp
#include "example.h"
double g_var = 17.001;
int add(int a, int b)
{
return a + b;
}
Foo::Foo()
: m_num(-1)
{
}
int Foo::GetNum() const
{
return m_num;
}
void Foo::SetNum(int num)
{
m_num = num;
}
为了使用SWIG导出该C++接口,我们需要为其定义接口文件:
// example.i
%module example // 模块名称
%{
#include "example.h" // %{ }%包围的代码不会被解析,直接插入到所生成的example_wrap.cxx中
%}
extern double g_var;
int add(int a, int b);
class Foo
{
public:
Foo();
int GetNum() const;
void SetNum(int num);
private:
int m_num;
};
可以看到,接口文件只需要对C++头文件做少许改动即可(但是如果需要处理指针,传值参数等情况时需要较大改动)。
执行命令swig -c++ -python example.i,SWIG生成了 两个文件:example.py和example_wrap.cxx。example_wrap.cxx是一个使用Python CAPI实现的模块,主要用于转接example.cpp中的函数,example.py是一个为了让我们可以用OO方式使用该模块的中间层,稍后我们会详细分析SWIG的封装方式。
为了简单,使用distutils将生成的wrapper文件和原example.cpp文件一起编译,定义文件setup.py:
from distutils.core import setup, Extension
setup(name='example', ext_modules=[Extension("_example", ["example.cpp", "example_wrap.cxx"],
extra_compile_args=['-g'])])
执行命令:
python setup.py build
将生成的build/…/_example.so移到当前目录,进入Python:
>>> import example
>>> example.cvar.g_var # 全局变量放在cvar中
17.001
>>> example.add(1, 2)
3
>>> f = example.Foo()
>>> f.GetNum()
-1
>>> f.SetNum(11)
>>> f.GetNum()
11
可以看到,无论是变量、函数还是类定义,SWIG都能很容易地实现导出。
3. 封装浅析
SWIG是如何实现导出的呢?首先来看全局变量的导出方式,SWIG将所有的全局变量导出在module_name.cvar里。在example_wrap.cxx中CAPI初始化函数中:
int g_var_set(PyObject *_val)
{
double val;
int res = SWIG_AsVal_double(_val, &val); // 从PyObject解析int
g_var = static_cast< double >(val);
return 0;
}
PyObject *g_var_get(void) {
PyObject *pyobj = 0;
pyobj = SWIG_From_double(static_cast< double >(g_var)); // 使用int建立PyObject
return pyobj;
}
#define SWIG_INIT init_example
extern "C" void SWIG_INIT(void)
{
m = Py_InitModule((char *) SWIG_name, SwigMethods); // 注册导出函数
d = PyModule_GetDict(m);
PyDict_SetItemString(d,(char*)"cvar", SWIG_globals()); // _example.__dict__['cvar'] = xxx
SWIG_addvarlink(SWIG_globals(),(char*)"g_var",g_var_get, g_var_set); // 添加变量g_var
}
可以看到,SWIG使用getter和setter函数封装了变量g_var。SWIG_globals()返回一个单例PyObject,描述结构体为swig_varlink_type,并自定义了__getattr__和__setattr__方法。SWIG_addvarlink在c_var中添加了g_var,cvar的__getattr__和__setattr__方法负责分发访问。
然后看一下SWIG如何对函数导出的:
PyObject *_wrap_add(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
int arg1 ;
int arg2 ;
int result;
int val1 ;
int val2 ;
PyObject * obj0 = 0 ;
PyObject * obj1 = 0 ;
if (!PyArg_ParseTuple(args,(char *)"OO:add",&obj0,&obj1)) SWIG_fail;
SWIG_AsVal_int(obj0, &val1);
arg1 = static_cast< int >(val1);
SWIG_AsVal_int(obj1, &val2);
arg2 = static_cast< int >(val2);
result = (int)add(arg1,arg2);
resultobj = SWIG_From_int(static_cast< int >(result));
return resultobj;
}
static PyMethodDef SwigMethods[] = {
{ (char *)"add", _wrap_add, METH_VARARGS, NULL},
...
{ NULL, NULL, 0, NULL }
};
extern "C" void SWIG_init(void) {
PyObject *m = Py_InitModule((char *) SWIG_name, SwigMethods); // 注册导出函数
...
}
Python CAPI本身就支持函数导出,SWIG的实现也很清晰,不在赘述。
然后是类Foo的导出,先来看一下Python中Foo对象的一些信息:
>>> f
<example.Foo; proxy of <Swig Object of type 'Foo *' at 0x5c8770> >
>>> type(f)
<class 'example.Foo'>
>>> f.this
<Swig Object of type 'Foo *' at 0x5c8770>
>>> type(f.this)
<type 'PySwigObject'>
SWIG使用PySwigObject来保存对象C++指针以及指针类型(swig_type_info):
typedef struct {
PyObject_HEAD
void *ptr;
swig_type_info *ty;
int own;
PyObject *next;
} PySwigObject;
我们对Foo对象的操作可以总结为几种:构造、删除、调用函数。SWIG将这几种操作封装成普通函数导出:
static PyMethodDef SwigMethods[] = {
{ (char *)"new_Foo", _wrap_new_Foo, METH_VARARGS, NULL},
{ (char *)"Foo_GetNum", _wrap_Foo_GetNum, METH_VARARGS, NULL},
{ (char *)"Foo_SetNum", _wrap_Foo_SetNum, METH_VARARGS, NULL},
{ (char *)"delete_Foo", _wrap_delete_Foo, METH_VARARGS, NULL},
{ NULL, NULL, 0, NULL }
};
调用封装函数_wrap_*时(_wrap_new_Foo除外),将携带指针及类型信息的PySwigObject作为参数传递(以_wrap_Foo_GetNum为例):
PyObject *_wrap_Foo_GetNum(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
Foo *arg1 = (Foo *) 0 ;
int result;
void *argp1 = 0 ;
PyObject * obj0 = 0 ;
PyArg_ParseTuple(args,(char *)"O:Foo_GetNum",&obj0); // obj0即Python中Foo对象的self
SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Foo, 0 | 0 ); // 从PySwigObject(self.this)中拿到指针
arg1 = reinterpret_cast< Foo * >(argp1);
result = (int)((Foo const *)arg1)->GetNum();
resultobj = SWIG_From_int(static_cast< int >(result));
return resultobj;
}
为了方便调用(或者说实现OO式调用),SWIG使用Shadow Class进一步对其进行了封装,example.py中定义了一个Foo类,并在其成员函数中调用_example中导出的函数:
class Foo(_object):
def __init__(self, *args):
this = _example.new_Foo(*args) // 新建Foo,保存在self.this中
try: self.this.append(this)
except: self.this = this
def GetNum(*args): return _example.Foo_GetNum(*args)
def SetNum(*args): return _example.Foo_SetNum(*args)
__swig_destroy__ = _example.delete_Foo // _example.so会调用
__del__ = lambda self : None;
对于很多复杂类型,例如数组,引用等,SWIG都使用指针来表示,方法与类封装类似。如果希望使用其他方式,SWIG提供了typemap来实现自定义类型传递控制,具体参见SWIG手册。
总结:
本文只是介绍了SWIG Python对C+的基本使用方法,还有很多内容没有涉及,例如操作符,继承、多态、模板等。由于C+本身复杂的特性,要支持这些,使用SWIG时还需要注意一些繁琐的东西。
另外如果想要了解SWIG实现交互式调试的方法,可以参考使用Swig Python动态绑定C++对象
睿初科技软件开发技术博客,转载请注明出处
blog comments powered by Disqus
发布日期
标签
最近发表
- volatile与多线程
- TDD practice in UI: Develop and test GUI independently by mockito
- jemalloc源码解析-核心架构
- jemalloc源码解析-内存管理
- boost::bind源码分析
- 小试QtTest
- 一个gtk下的目录权限问题
- Django学习 - Model
- Code snippets from C & C++ Code Capsule
- Using Eclipse Spy in GUI products based on RCP
文章分类
- cpp 3
- wxwidgets 4
- swt/jface 1
- chrome 3
- memory_management 5
- eclipse 1
- 工具 4
- 项目管理 1
- cpplint 1
- 算法 1
- 编程语言 1
- python 5
- compile 1
- c++ 7
- 工具 c++ 1
- 源码分析 c++ 3
- c++ boost 2
- data structure 1
- wxwidgets c++ 1
- template 1
- boost 1
- wxsocket 1
- wxwidget 2
- java 2
- 源码分析 1
- 网路工具 1
- eclipse插件 1
- django 1
- gtk 1
- 测试 1
- 测试 tdd 1
- multithreading 1