SWIG给C结构增加‘成员函数’
以前一直以为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
3typedef struct Vector {
double x, y, z;
} Vector;
这样封装的话在python中只能这样调用:1
2
3
4
51.0, 2.0, 3.0); v = Vector(
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// file : vector.i
%module modulename
%{
%}
%include "vector.h" // 在这个.i文件中宏展开,让后面的函数知道结构定义
%extend Vector { // 将里面的函数绑定到Vector结构,如果没有typedef的话要写成struct Vector
// 类似C++的构造函数,但是要进行动态内存分配
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
8from vector import *
1, 2, 3) v = Vector(
v.print()
[1, 2, 3]
del v # 删除此对象的引用
在定义结构的时候直接添加成员函数
1 | // file : vector.i |
使用已定义的函数作为结构的成员函数
如果在源代码中的实现文件中已经定义结构的接口函数(名称要按照swig的规定),则可以直接在结构定义中扩展这些函数。
例如如果我在实现文件中定义了如下几个函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// file : vector.c
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// file : vector.i
%module modulename
%{
%}
typedef struct Vector {
double x, y, z;
%extend {
Vector(int, int, int); // 这个函数将会调用实现文件中的new_Vector()
~Vector(); // 将会调用delete_Vector()
void print(); // 将调用Vector_print()
}
};
但是这里的命名是由要求的,因为在python中或者其他目标语言中调用成员方法是通过调用
Vector_print
来实现的,因此如果要认为定义成员函数,函数的名称也要符合这些规则。