以前一直以为C不支持面向对象,为python写扩展真是太憋屈了,但是今天看了SWIG的文档,原来人家早就解决了这个问题。
由于之前看的是《Python科学计算》上面关于swig的简单介绍,上面说了如何在python中如何像类一样的使用C结构,但是也只是停留在如何使用结构的成员数据上,但是没有讲如何将C的函数封装成python的类方法的方法。相反由于C++有原生OOP的支持,固然swig直接可以将C++的类封装成python的类。所以我以前一直认为这时候只能用C++了。
但其实,还是读书读的少啊。swig这么强大的封装工具,这早就想到了。毕竟OO是一种思想,C照样能写出OO的代码来(多重继承和多态这种行么?)。
所以今天看到swig文档中关于%extend
指令的介绍才感觉相见恨晚啊。
给C结构添加成员函数
C语言的OO通常都是写操作结构的接口函数,尽管SWIG为C++提供了直接的面向对象的类映射,C却没有。尽管如此SWIG还是提供了一个%extend
指令,就是这个指令能够将C语言封装成支持OO的语言的接口。
向已定义的结构添加成员函数
具体的使用方法:
1 2 3
| typedef struct Vector { double x, y, z; } Vector;
|
这样封装的话在python中只能这样调用:
1 2 3 4 5
| >>> v = Vector(1.0, 2.0, 3.0); >>> v.x >>> 1.0
|
但是这个类并没有成员函数,那如何给这个结构绑定函数,并封装成python类的成员函数呢?上%extend
!
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 30 31 32
| %module modulename %{ #include "vector.h" %} %include "vector.h" %extend Vector { Vector(double x, double y, double z) { Vector * v; v = (Vector *)malloc(sizeof(Vector)); v->x = x; v->y = y; v->z = z; } ~Vector() { free($self); } void print() { printf("Vector [%g, %g, %g]\n", $self->x, $self.y, $self->z); } };
|
是的,通过上面的代码可以看出SWIG在模仿C++的语法,其中构造函数和析构函数就不多说了。$self
是一个内置的变量,作用类似于C++中的this
,也就是指向当前对象的指针。
%extend
指令不仅定义了成员函数,而且还相当于在vector.i
文件中对这些函数进行了声明,这样不仅C编译器会编译这些函数,SWIG也会处理这些函数生成接口代码。
这样我们在Python中就可以这样使用:
1 2 3 4 5 6 7 8
| >>> from vector import * >>> v = Vector(1, 2, 3) >>> v.print() [1, 2, 3] >>> del v
|
在定义结构的时候直接添加成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| %module modulename %{ #include "vector.h" %} typedef struct Vector { double x, y, z; %extend { Vector(double x, double y, double z) { ... } ~Vector() { ... } ... } } Vector;
|
使用已定义的函数作为结构的成员函数
如果在源代码中的实现文件中已经定义结构的接口函数(名称要按照swig的规定),则可以直接在结构定义中扩展这些函数。
例如如果我在实现文件中定义了如下几个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include "vector.h" Vector * new_Vector(double x, double y, double z) { Vector * v; v = (Vector *)malloc(sizeof(Vector)); v->x = x; v->y = y; v->z = z; return v; } void delete_Vector(Vector * v) { free(v); } void Vector_print(Vector * v) { printf("Vector [%g, %g, %g]\n", $self->x, $self.y, $self->z); }
|
然后我在*.i
文件中给SWIG声明Vector类的时候就可以直接添加成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| %module modulename %{ #include "vector.h" %} typedef struct Vector { double x, y, z; %extend { Vector(int, int, int); ~Vector(); void print(); } };
|
这里感觉和C++很像了,有了`%extend`的struct就相当于直接在头文件中定义类(包括成员数据和成员函数),然后类成员函数的实现在实现代码中。
但是这里的命名是由要求的,因为在python中或者其他目标语言中调用成员方法是通过调用Vector_print
来实现的,因此如果要认为定义成员函数,函数的名称也要符合这些规则。