当前位置:   article > 正文

​利用SWIG将C++库转为Python_swig c++转python

swig c++转python

作者/梁汝波

  1. 导读
  2. 途径
  3. SWIG的工作方式
  4. SWIG对Python支持到何种程度?
  5. 版本说明
  6. SWIG文档说明
  7. SWIG包含的内容
  8. 一个简单示例
  9. SWIG生成代码说明
  10. SWIG接口文件的结构
  11. 常用功能说明
  12. 模块
  13. 函数及回调函数
  14. 全局变量
  15. 常量和枚举变量
  16. 指针,引用,值和数组
  17. 结构和类,以及继承
  18. 重载
  19. 运算符重载
  20. 名字空间
  21. 模板
  22. 智能指针
  23. 引用记数对象支持
  24. 内存管理
  25. 跨语言多态
  26. 类型映射(typemap)
  27. SWIG库
  28. 参考资料

导读

如果你也像我们一样,同时使用Python和C++,以获得两种语言的优势,一定也会希望寻找一种好的方式集成这两种语言,相比而言,让Python能够方便使用C++的库更加重要,我们选择SWIG来实现这一需求,原因请见”途径”一节对几种实现途径的比较。

这篇博文介绍使用SWIG将C++库包装成Python接口,建议将”常用功能说明”之后的内容当做参考使用,因为那些内容牵涉到C++语言的各个特性,但不影响对SWIG整体使用的理解,可以在需要时参考。

另外,这篇博文中有很多例示代码,解释不多。是因为我觉得例示代码本身是很好的解释,清楚、准确、简练。如有问题,欢迎留言交流。

途径

为C++库提供Python接口有以下几种常见途径:
* Python C API
Python解释器提供的一组C API,利用这组API,可以用C/C++实现Python module,也可以将Python解释器做为一个脚本引擎嵌入到C/C++程序中,为C/C++程序提供运行Python脚本的能力。Python C API是其他途径的基础,其他途径最终都以某种方式以Python C API实现。然而,直接使用Python C API相当繁琐,容易出错,因此很少直接使用。
* ctypes
ctypes是Python标准库提供的调用动态链接库的模块,使用这个模块可以直接在Python里加载动态链接库,调用其中的函数。使用ctypes 的优势是门槛低,不用编写或修改C/C++代码。然而我只简单地使用过这种方式,没有深入研究,不了解它对C/C++的支持是否完整。
* Boost.Python
Boost.Python是Boost提供的一个C++的模板库,用以支持Python和C++的无缝互操作。相对SWIG来说,这个库的优势是功能通过C++ API完成,不用学习写新的接口文件。对C++的支持更自然、完整。这个库的问题是:1)有外部依赖;2)文档不好,我看到有人说他看到三个不同的Boost.Python的tutorial,而这三个tutorial却完全不一样。我花了两个小时尝试Boost.Python,连tutorial的例子都没跑通,就放弃了。
* SWIG
SWIG是本文描述的重点,也是我们采用的途径。SWIG完整支持ANSI C,支持除嵌套类外的所有C++特性。SWIG是一个接口编译器,旨在为C/C++方便地提供脚本语言接口。SWIG不仅可以为C/C++程序生成 Python接口,目前可以生成CLISP,Java,Lua,PHP,Ruby,Tcl等19种语言的接口。SWIG被Subversion, wxPython, Xapian等项目使用。值得一提的是,Google也使用SWIG。

SWIG的工作方式

SWIG本质上是个代码生成器,为C/C++程序生成到其他语言的包装代码(wrapper code),这些包装代码里会利用各语言提供的C API,将C/C++程序中的内容暴露给相应语言。为了生成这些包装代码,SWIG需要一个接口描述文件,描述将什么样的接口暴露给其他语言。SWIG的 接口描述文件可以包含以下内容:1)ANSI C函数原型声明 2)ANSI C变量声明 3) SWIG指示器(directive)相关内容。SWIG可以直接接受”.h”头文件做为接口描述文件。在有了接口描述文件后,就可以利用swig命令生 成包装代码了,然后将包装代码编译链接成可被其他语言调用的库。

SWIG对Python支持到何种程度?

利用SWIG,可以现实以下功能:1)用Python调用C/C++库;2)用Python继承C++类,并在Python中使用该继承类;3)C++使用Python扩展(通过文档描述应该可以支持,未验证)

版本说明

SWIG的最新版本为2.0.1。因为我们现在使用的SWIG版本为1.3.40,本篇博客里的说明仅针对1.3.40版

SWIG文档说明

SWIG的文档非常详尽,我甚至觉得太过详尽,不可能全看。我刚开始因为对SWIG文档组织不熟悉,看完一部分SWIG Basices就开始尝试,一路摸索到可以使用,后来才发现SWIG还有针对Python的专门文档。相比之下我之前摸索到的方案相当丑陋。

SWIG文档大体分两部分,一部分为SWIG本身:SWIG基本使用,对C及C++的支持,SWIG库及扩展等;另一部分为SWIG对每一个目标语言的文档,如SWIG和Python的文档。我建议只看和具体语言相关的文档,遇到问题时再去看SWIG本身的相关部分。

这篇博文应该会描述到用SWIG对C++进行Python包装的各个方面,不过喜欢原汁原味且有充足时间又comfortable with English的同学可直接看SWIG的文档。

SWIG包含的内容

SWIG包含以下几部分内容:

  • 一个代码生成器(swig):代码生成器根据接口说明文件,生成相应的包装代码。
  • 一个库:SWIG将常用的内容放到SWIG库里了,比如对数组、指针的支持,字符串支持,STL支持等。可以在接口文件中直接引用库里的内容,大大方便接口文件的编写。

一个简单示例

本节给出一个简单示例,提供对SWIG的直观认识,文章末尾处给出了一个更完整的例子。

  1. example.h
  2. #include <iostream>
  3. using namespace std;
  4. class Example{
  5. public:
  6. void say_hello();
  7. };

  1. example.cpp
  2. #include "example.h"
  3. void Example::say_hello(){
  4. cout<<"hello"<<endl;
  5. }

  1. example.i
  2. %module example
  3. %{
  4. #include "example.h"
  5. %}
  6. %include "example.h"

  1. setup.py
  2. #!/usr/bin/env python
  3. """
  4. setup.py file for SWIG C\+\+/Python example
  5. """
  6. from distutils.core import setup, Extension
  7. example_module = Extension('_example',
  8. sources=['example.cpp', 'example_wrap.cxx',],
  9. )
  10. setup (name = 'example',
  11. version = '0.1',
  12. author = "www.99fang.com",
  13. description = """Simple swig C\+\+/Python example""",
  14. ext_modules = [example_module],
  15. py_modules = ["example"],
  16. )

运行以下命令:

swig -c\+\+ -python example.i
python setup.py build_ext --inplace

如果编译无误的话,就可以测试啦:

>>> import example
>>> example.Example().say_hello()
hello

以上我用distutils构建了example module,也可以通过编译器直接构建, 比如:

gcc -fPIC -I/usr/include/python2.5/ -lstdc\+\+ -shared -o _example.so example_wrap.cxx example.cpp

注意,-fPIC和-lstdc++都是必要的。_example.so前的’_'也是必要的。

SWIG生成代码说明

“swig -c++ -python example.i”命令生成了两个文件:example_wrap.cxx, example.py。example_wrap.cxx里会对Example类提供类使以下的扁平接口:

Example* new_Example();

void say_hello(Example* example);

viod delete_Example(Example *example);

这个接口被编译到_example.so里,_example可以做为一个Python module直接加载到Python解释器中。example.py利用_example里提供的接口,将扁平的接口还原为Python的Example 类,这个类做为C++ Example类的代理类型,这样使用方式就更加自然了。

SWIG接口文件的结构

SWIG接口文件指导SWIG生成包装代码,其中包含%module声明,接口声明(%include “example.h”),以及%{ … %}中的内容。%{ … %}中的内容会原封不动地拷贝到生成的包装代码中,上节例子中的#include “example.h”是必要的,因为接口声明中仅是声明接口中要暴露哪些内容(Example类),但如果没有#include “example.h”的话,生成的包装代码是无法通过编译的。

常用功能说明

* 处理输入输出参数

C++包装的一个常见问题是有的C++函数以指针做为函数参数, 如:

  1. void add(int x, int y, int *result) {
  2. *result = x + y;
  3. }
  4. int sub(int *x, int *y) {
  5. return *x-*y;
  6. }

处理这种情况的最方便方式是使用SWIG库里的typemaps.i (关于SWIG库和Typemap见之后内容):

  1. %module example
  2. %include "typemaps.i"
  3. void add(int, int, int *OUTPUT);
  4. int sub(int *INPUT, int *INPUT);
  5. >>> a = add(3,4)
  6. >>> print a
  7. 7
  8. >>> b = sub(7,4)
  9. >>> print b
  10. 3

另一种写法:

  1. %module example
  2. %include "typemaps.i"
  3. %apply int *OUTPUT { int *result };
  4. %apply int *INPUT { int *x, int *y};
  5. void add(int x, int y, int *result);
  6. int sub(int *x, int *y);

对于既是输入又是输出参数的处理:

  1. void negate(int *x) {
  2. *x = -(*x);
  3. }
  4. -----------------------------
  5. %include "typemaps.i"
  6. ...
  7. void negate(int *INOUT);
  8. -----------------------------
  9. >>> a = negate(3)
  10. >>> print a
  11. -3

对于多个返回参数的处理:

  1. /* send message, return number of bytes sent, along with success code */
  2. int send_message(char *text, int len, int *success);
  3. -----------------------------
  4. %module example
  5. %include "typemaps.i"
  6. %apply int *OUTPUT { int *success };
  7. ...
  8. int send_message(char *text, int *success);
  9. -----------------------------
  10. bytes, success = send_message("Hello World")
  11. if not success:
  12. print "Whoa!"
  13. else:
  14. print "Sent", bytes

当输出都通过参数给出情况的处理:

  1. void get_dimensions(Matrix *m, int *rows, int *columns);
  2. %module example
  3. %include "typemaps.i"
  4. %apply int *OUTPUT { int *rows, int *columns };
  5. ...
  6. void get_dimensions(Matrix *m, int *rows, *columns);
  7. >>> r,c = get_dimensions(m)

注意,typemaps.i只支持了基本数据类型,所以不能写void foo(Bar *OUTPUT);,因为typemaps.i里没有对Bar定义OUTPUT规则。

* C数组实现

有的C函数要求传入一个数组作为参数,调用这种函数时不能直接传入一个Python list或tuple, 有三种方式能解决这个问题:

  1. 使用类型映射(Typemap), 将数组代码生成为Python list或tuple相应代码
  2. 使用辅助函数,用辅助函数生成和操作数组对象,再结合在接口文件中插入一些Python代码,也可使Python直接传入list或tuple。这种方式在之后说明。
  3. 使用SWIG库里的carrays.i

这里先介绍carrays.i方式:

  1. int sumitems(int *first, int nitems) {
  2. int i, sum = 0;
  3. for (i = 0; i < nitems; i\+\+) {
  4. sum += first[i];
  5. }
  6. return sum;
  7. }
  8. -----------------------------
  9. %include "carrays.i"
  10. %array_class(int, intArray);
  11. -----------------------------
  12. >>> a = intArray(10000000) # Array of 10-million integers
  13. >>> for i in xrange(10000): # Set some values
  14. ... a[i] = i
  15. >>> sumitems(a,10000)
  16. 49995000

通过%array_class创建出来的数组是C数组的直接代理,非常底层和高效,但是,它也和C数组一样不安全,一样没有边界检查。

* C/C++辅助函数

可以通过辅助函数来完一些SWIG本身不支持的功能。事实上,辅助函数可谓SWIG包装的瑞士军刀,一旦了解它使用,你可以使SWIG支持几乎所有你需要的功能,不过提醒一下,有很多C++特性是SWIG本身支持或者通过库支持的,不需要通过辅助函数实现。

同样的,直接上例示代码:

  1. void set_transform(Image *im, double m[4][4]);
  2. >>> a = [
  3. ... [1,0,0,0],
  4. ... [0,1,0,0],
  5. ... [0,0,1,0],
  6. ... [0,0,0,1]]
  7. >>> set_transform(im,a)
  8. Traceback (most recent call last):
  9. File "<stdin>", line 1, in ?
  10. TypeError: Type error. Expected _p_a_4__double

可以看到,set_transform是不能接受Python二维List的,可以用辅助函数帮助实现:

  1. %inline %{
  2. /* Note: double[4][4] is equivalent to a pointer to an array double (*)[4] */
  3. double (*new_mat44())[4] {
  4. return (double (*)[4]) malloc(16*sizeof(double));
  5. }
  6. void free_mat44(double (*x)[4]) {
  7. free(x);
  8. }
  9. void mat44_set(double x[4][4], int i, int j, double v) {
  10. x[i][j] = v;
  11. }
  12. double mat44_get(double x[4][4], int i, int j) {
  13. return x[i][j];
  14. }
  15. %}
  16. >>> a = new_mat44()
  17. >>> mat44_set(a,0,0,1.0)
  18. >>> mat44_set(a,1,1,1.0)
  19. >>> mat44_set(a,2,2,1.0)
  20. ...
  21. >>> set_transform(im,a)
  22. >>>

当然,这样使用起来还不够优雅,但可以工作了,接下来介绍通过插入额外的Python代码来让使用优雅起来。

*插入额外的Python代码

为了让set_transform函数接受Python二维list或tuple,我们可以对它的Python代码稍加改造:

  1. void set_transform(Image *im, double x[4][4]);
  2. ...
  3. /* Rewrite the high level interface to set_transform */
  4. %pythoncode %{
  5. def set_transform(im,x):
  6. a = new_mat44()
  7. for i in range(4):
  8. for j in range(4):
  9. mat44_set(a,i,j,x[i][j])
  10. _example.set_transform(im,a)
  11. free_mat44(a)
  12. %}
  13. >>> a = [
  14. ... [1,0,0,0],
  15. ... [0,1,0,0],
  16. ... [0,0,1,0],
  17. ... [0,0,0,1]]
  18. >>> set_transform(im,a)

SWIG还提供了%feature(“shadow”), %feature(“pythonprepend”), %feature(“pythonappend”)来支持重写某函数的代理函数,或在某函数前后插入额外代码,在%feature(“shadow”)中 可用$action来指代对C++相应函数的调用:

  1. %module example
  2. // Rewrite bar() python code
  3. %feature("shadow") Foo::bar(int) %{
  4. def bar(*args):
  5. #do something before
  6. $action
  7. #do something after
  8. %}
  9. class Foo {
  10. public:
  11. int bar(int x);
  12. }
或者:


  1. %module example
  2. // Add python code to bar()
  3. %feature("pythonprepend") Foo::bar(int) %{
  4. #do something before C\+\+ call
  5. %}
  6. %feature("pythonappend") Foo::bar(int) %{
  7. #do something after C\+\+ call
  8. %}
  9. class Foo {
  10. public:
  11. int bar(int x);
  12. }

*用%extend指示器扩展C++类

你可以通过%extend指示器扩展C++类,甚至可用通过这种方式重载Python运算符:

  1. %module example
  2. %{
  3. #include "someheader.h"
  4. %}
  5. struct Vector {
  6. double x,y,z;
  7. };
  8. %extend Vector {
  9. char *__str__() {
  10. static char tmp[1024];
  11. sprintf(tmp,"Vector(%g,%g,%g)", $self->x,$self->y,$self->z);
  12. return tmp;
  13. }
  14. Vector(double x, double y, double z) {
  15. Vector *v = (Vector *) malloc(sizeof(Vector));
  16. v->x = x;
  17. v->y = y;
  18. v->z = z;
  19. return v;
  20. }
  21. Vector __add__(Vector *other) {
  22. Vector v;
  23. v.x = $self->x + other->x;
  24. v.y = $self->y + other->y;
  25. v.z = $self->z + other->z;
  26. return v;
  27. }
  28. };
  29. >>> v = example.Vector(2,3,4)
  30. >>> print v
  31. Vector(2,3,4)
  32. >>> v = example.Vector(2,3,4)
  33. >>> w = example.Vector(10,11,12)
  34. >>> print v+w
  35. Vector(12,14,16)

注意,在%extend里this用$self代替。

* 字符串处理

SWIG将char *映射为Python的字符串,但是Python字符串是不可修改的(immutable),如果某函数有修改char *,很可能导致Python解释器崩溃。对由于这种情况,可以使用SWIG库里的cstring.i。

模块

SWIG通过%module指示器指定Python模块的名字

函数及回调函数

全局函数被包装为%module指示模块下的函数,如:

  1. %module example
  2. int add(int a, int b);
  3. >>>import example
  4. >>>print example.add(3, 4)
  5. 7

全局变量

SWIG创建一个特殊的变量’cvar’来存取全局变量,如:

  1. %module example
  2. %inline %{
  3. double density = 2.5;
  4. %}
  5. >>>import example
  6. >>>print example.cvar.density
  7. 2.5

inline是另一个常见的SWIG指示器,用来在接口文件中插入C/C++代码,并将代码中声明的内容输出到接口中。

常量和枚举变量

用#define, enum或者%constant指定常量:

  1. #define PI 3.14159
  2. #define VERSION "1.0"
  3. enum Beverage { ALE, LAGER, STOUT, PILSNER };
  4. %constant int FOO = 42;
  5. %constant const char *path = "/usr/local";

指针,引用,值和数组

SWIG完整地支持指针:

  1. %module example
  2. FILE *fopen(const char *filename, const char *mode);
  3. int fputs(const char *, FILE *);
  4. int fclose(FILE *);
  5. >>> import example
  6. >>> f = example.fopen("junk","w")
  7. >>> example.fputs("Hello World\n", f)
  8. >>> example.fclose(f)
  9. >>> print f
  10. <Swig Object at _08a71808_p_FILE>
  11. >>> print str(f)
  12. _c0671108_p_FILE
  13. 指针的裸值可以通过将指针对象转换成int获得,不过,无法通过一个int值构造出一个指针对象。
  14. >>> print int(f)
  15. 135833352

’0′或NULL被表示为None.

对指针的类型转换或运算必须通过辅助函数完成,特殊要注意的是,对C++指针的类型转换,应该用C++方式的转换,而不是用C方式的转换,因为在转换无法完成是,C++方式的转换会返回NULL,而C方式的转换会返回一个无效的指针:

  1. %inline %{
  2. /* C-style cast */
  3. Bar *FooToBar(Foo *f) {
  4. return (Bar *) f;
  5. }
  6. /* C\+\+-style cast */
  7. Foo *BarToFoo(Bar *b) {
  8. return dynamic_cast<Foo*>(b);
  9. }
  10. Foo *IncrFoo(Foo *f, int i) {
  11. return f+i;
  12. }

在C++中,函数参数可能是指针,引用,常量引用,值,数据等,SWIG将这些类型统一为指针类型处理(通过相应的包装代码):

  1. void spam1(Foo *x); // Pass by pointer
  2. void spam2(Foo &x); // Pass by reference
  3. void spam3(const Foo &x);// Pass by const reference
  4. void spam4(Foo x); // Pass by value
  5. void spam5(Foo x[]); // Array of objects
  6. >>> f = Foo() # Create a Foo
  7. >>> spam1(f) # Ok. Pointer
  8. >>> spam2(f) # Ok. Reference
  9. >>> spam3(f) # Ok. Const reference
  10. >>> spam4(f) # Ok. Value.
  11. >>> spam5(f) # Ok. Array (1 element)

返回值是也同样的:

  1. Foo *spam6();
  2. Foo &spam7();
  3. Foo spam8();
  4. const Foo &spam9();

这些函数都会统一为返回一个Foo指针。

结构和类,以及继承

结构和类是以Python类来包装的:

  1. struct Vector {
  2. double x,y,z;
  3. };
  4. >>> v = example.Vector()
  5. >>> v.x = 3.5
  6. >>> v.y = 7.2
  7. >>> print v.x, v.y, v.z
  8. 7.8 -4.5 0.0
  9. >>>

如果类或结构中包含数组,该数组是通过指针来操纵的:

  1. struct Bar {
  2. int x[16];
  3. };
  4. >>> b = example.Bar()
  5. >>> print b.x
  6. _801861a4_p_int
  7. >>>
  8. 对于数组赋值,SWIG会做数据的值拷贝:
  9. >>> c = example.Bar()
  10. >>> c.x = b.x # Copy contents of b.x to c.x
  11. 但是,如果一个类或结构中包含另一个类或结构成员,赋值操作完全和指针操作相同。

对于静态类成员函数,在Python中有三种访问方式:

  1. class Spam {
  2. public:
  3. static int bar;
  4. static void foo();
  5. };
  6. >>> example.Spam_foo() # Spam::foo()
  7. >>> s = example.Spam()
  8. >>> s.foo() # Spam::foo() via an instance
  9. >>> example.Spam.foo() # Spam::foo(). Python-2.2 only

其中第三种方式Python2.2及以上版本才支持,因为之前版本的Python不支持静态类成员函数。

静态类成员变量以全局变量方式获取:

>>> print example.cvar.Spam_bar

SWIG支持C++继承,可以用Python工具函数验证这一点:

  1. class Foo {
  2. ...
  3. };
  4. class Bar : public Foo {
  5. ...
  6. };
  7. >>> b = Bar()
  8. >>> instance(b,Foo)
  9. 1
  10. >>> issubclass(Bar,Foo)
  11. 1
  12. >>> issubclass(Foo,Bar)
  13. 0

同时,如果有形如void spam(Foo *f);的函数,可以传b = Bar()进去。

SWIG支持多继承。

重载

SWIG支持C++重载:

  1. void foo(int);
  2. void foo(char *c);
  3. >>> foo(3) # foo(int)
  4. >>> foo("Hello") # foo(char *c)

但是,SWIG不能支持所有形式的C++重载,如:

  1. void spam(int);
  2. void spam(short);
  3. void foo(Bar *b);
  4. void foo(Bar &b);
  5. 这种形式的声明会让SWIG产生警告,可以通过重名命或忽略其中一个来避免这个警告:
  6. %rename(spam_short) spam(short);
  7. %ignore spam(short);

运算符重载

SWIG能够自动处理运算符重载:

  1. class Complex {
  2. private:
  3. double rpart, ipart;
  4. public:
  5. Complex(double r = 0, double i = 0) : rpart(r), ipart(i) { }
  6. Complex(const Complex &c) : rpart(c.rpart), ipart(c.ipart) { }
  7. Complex &operator=(const Complex &c);
  8. Complex operator+=(const Complex &c) const;
  9. Complex operator+(const Complex &c) const;
  10. Complex operator-(const Complex &c) const;
  11. Complex operator*(const Complex &c) const;
  12. Complex operator-() const;
  13. double re() const { return rpart; }
  14. double im() const { return ipart; }
  15. };
  16. >>> c = Complex(3,4)
  17. >>> d = Complex(7,8)
  18. >>> e = c + d
  19. >>> e.re()
  20. 10.0
  21. >>> e.im()
  22. 12.0
  23. >>> c += d
  24. >>> c.re()
  25. 10.0
  26. >>> c.im()
  27. 12.0

如果重载的运算符不是类的一部分,SWIG无法直接支持,如:

  1. class Complex {
  2. ...
  3. friend Complex operator+(double, const Complex &c);
  4. ...
  5. };

这种情况下SWIG是报一个警告,不过还是可以通过一个特殊的函数,来包装这个运算符:

%rename(Complex_add_dc) operator+(double, const Complex &);

不过,有的运算符无法清晰地映射到Python表示,如赋值运算符,像这样的重载会被忽略。

名字空间

名字空间不会映射成Python的模块名,如果不同名字空间有同名实体要暴露到接口中,可以通过重命名指示器解决:

  1. %rename(Bar_spam) Bar::spam;
  2. namespace Foo {
  3. int spam();
  4. }
  5. namespace Bar {
  6. int spam();
  7. }

模板

SWIG对C/C++的包装是二进制级别的,但C++模板根本不是二进制级别的概念,所以对模板的包装需要将模板实例化,SWIG提供%template指示器支持这项功能:

  1. %module example
  2. %{
  3. #include "pair.h"
  4. %}
  5. template<class T1, class T2>
  6. struct pair {
  7. typedef T1 first_type;
  8. typedef T2 second_type;
  9. T1 first;
  10. T2 second;
  11. pair();
  12. pair(const T1&, const T2&);
  13. ~pair();
  14. };
  15. %template(pairii) pair<int,int>;
  16. >>> import example
  17. >>> p = example.pairii(3,4)
  18. >>> p.first
  19. 3
  20. >>> p.second
  21. 4

如果你要同时映射一个模板,以及以这个模板为参数的另一个模板,还要做一点特殊的工作, 比如,同时映射pair< string, string >和 vector< pair <string, string> >,需要像下面这样做:

  1. %module testpair
  2. %include "std_string.i"
  3. %include "std_vector.i"
  4. %include "std_pair.i"
  5. %{
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. using namespace std;
  10. %}
  11. %template(StringPair) std::pair<std::string ,std::string>;
  12. SWIG_STD_VECTOR_SPECIALIZE_MINIMUM(StringPair, std::pair< std::string, std::string >);
  13. %template(StringPairVector) std::vector< std::pair<std::string, std::string> >;

遗憾的是,我并没有在文档中发现对这种做法的说明,以上做法是在swig用户组中问到的。

智能指针

有的函数的返回值是智能指针,为了调用这样的函数,只需要对智能指针类型做相应声明:

  1. %module example
  2. ...
  3. %template(SmartPtrFoo) SmartPtr<Foo>;
  4. ...
  5. >>> p = example.CreateFoo() # CreatFool()返回一个SmartPtr<Foo>
  6. >>> p.x = 3 # Foo::x
  7. >>> p.bar() # Foo::bar

可以通过p.__deref__()得到相应的Foo*

引用记数对象支持

对于使用引用记数惯例的C++对象,SWIG提供了%ref和%unref指示器支持,使用Python里使用时不用手工调用ref和unref函数。因为我们目前没有使用引用记数技术,具体细节这里不详述了。

内存管理

SWIG是通过在Python里创建C++相应类型的代理类型来包装C++的,每个Python代理对象里有一个.thisown的标志,这个标志 决定此代理对象是否负责相应C++对象的生命周期:如果.thisown这个标志为1,Python解释器在回收Python代理对象时也会销毁相应的 C++对象,如果没有这个标志或这个标志的值是0,则Python代理对象回收时不影响相应的C++对象。

当创建对象,或通过值返回方式获得对象时,代理对象自动获得.thisown标志。当通过指针方式获得对象时,代理对象.thisown的值为0:

  1. class Foo {
  2. public:
  3. Foo();
  4. Foo bar();
  5. Foo *spam();
  6. };
  7. >>> f = Foo()
  8. >>> f.thisown
  9. 1
  10. >>> g = f.bar()
  11. >>> g.thisown
  12. 1
  13. >>> f = Foo()
  14. >>> s = f.spam()
  15. >>> print s.thisown
  16. 0

当这种行为不是期望的行为的时候,可以人工设置这个标志的值:

>>> v.thisown = 0

跨语言多态

当你希望用Python扩展(继承)C++的类型的时候,你就需要跨语言多态支持了。SWIG提供了一个调度者(director)特性支持此功能,但此特性默认是关闭的,通过以下方式打开此特性:

首先,在module指示器里打开

%module(directors="1") modulename

其次,通过%feature指示器告诉SWIG哪些类和函数需要跨语言多态支持:

// generate directors for all classes that have virtual methods
%feature("director");         

// generate directors for all virtual methods in class Foo
%feature("director") Foo;      

// generate a director for just Foo::bar()
%feature("director") Foo::bar;

可以使用%feature(“nodirector”)指示器关闭某个类型或函数的的跨语言多态支持:

%feature("director") Foo;
%feature("nodirector") Foo::bar;

类型映射(Typemaps)

类型映射是SWIG最核心的一部分,类型映射就是告诉SWIG对某个C类型,生成什么样的代码。不过,SWIG的文档里说类型映射是SWIG的高级自定义部分,不是使用SWIG需要理解的,除非你要提升自己的NB等级 

以下的类型映射可用于将整数从Python转换为C:

%module example

%typemap(in) int {
    $1 = (int) PyLong_AsLong($input);
    printf("Received an integer : %d\n",$1);
}
%inline %{
int add(int a, int b){
    return a+b;
}
%}

>>> import example
>>> example.add(3,4)
Received an integer : 3
Received an integer : 4
7

SWIG库

SWIG提供了一组库文件,用以支持常用的包装,如数组,标准库等。可以在接口文件中引入这些库文件。比如,在%include “std_string.i”后,就可以直接给需要string参数数的函数传Python字符串了。对”std_vector.i”举例如下:

  1. %module example
  2. %include "std_vector.i"
  3. namespace std {
  4. %template(vectori) vector<int>;
  5. };
  6. >>> from example import *
  7. >>> v = vectori()
  8. >>> v.push_back(1)
  9. >>> print v.size()
  10. 1

参考资料

SWIG和Python: SWIG-1.3 Documentation
SWIG基础: SWIG-1.3 Documentation
SWIG和C++: SWIG-1.3 Documentation

swig 将c++转换为python 接口_jishuqianjin的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/555675
推荐阅读
相关标签
  

闽ICP备14008679号