SWIG与Python(下篇)

SWIG与Python(下篇)

过去的章节主要描述python包装的高级视角。包装的主要特点就是,类和结构体都是用python代理类实现的。这可以提供一个很自然的接口,允许SWIG支持很多高级特点,类如运算符重载。但是一些底层的细节被省略了,这一小节主要介绍代理类怎么工作的。

33.4.1 代理类

你有这么样的类:

class Foo {

public:

int x;

int spam(int);

他会被包装成:

Foo *new_Foo() {

return new Foo();

}

void delete_Foo(Foo *f) {

delete f;

}

int Foo_x_get(Foo *f) {

return f->x;

}

void Foo_x_set(Foo *f, int value) {

f->x = value;

}

int Foo_spam(Foo *f, int arg1) {

return f->spam(arg1);

}

这个wrapper可以低级扩展模块里被发现。(_example.so)

SWIG会使用这个wrapper来生成高级python代理类(浅类)

import _example

class Foo(object):

def __init__(self):

self.this = _example.new_Foo()

self.thisown = 1

def __del__(self):

if self.thisown:

_example.delete_Foo(self.this)

def spam(self,arg1):

return _example.Foo_spam(self.this,arg1)

x = property(_example.Foo_x_get, _example.Foo_x_set)

这个类仅仅拥有底层C++对象的指针(this),通过使用低级的代理配套函数来实现分发对象的变量和方法。从用户的角度来说,工作很正常。

>>> f = example.Foo()

>>> f.x = 3

>>> y = f.spam(5)

C++类被包装成python类实际上有很多优势,你可以为这个类增加新方法,你甚至还可以从他派生出一个新类!

33.4.2 内置类型

内置类型对于包装代码的性能提高帮助很大,为了理解代理类和内置类型的区别,我们看看两种情况下的对象。

当python使用代理类时,每一个包装对象都是一个纯的python类的实例。

class Foo(object):

def __init__(self):

self.this = _example.new_Foo()

self.thisown = 1

当创建一个Foo实例,调用new_Foo()会创建一个C++的Foo实例。

Python内置类型包含了一个C++实例,它叫做SwigPyObject,在python的Foo 对象里的this域存储了SwigPyObject实例。

所以python的Foo对象包含3部分:

1.      python实例

2.      SwigPyObject结构体的实例

3.      C++Foo对象

33.4.2.1限制

33.4.2.2运算符重载—尽管使用吧!

33.4.3内存管理

33.6普通个性化特性

这是最后一节,展示了C++包装的最基本细节。如果你只给SWIG一个头文件,你会获得同样类似的接口。但是为了产生一个好单元,这么做是远远不够的。功能的一些特点会被扔掉,一些功能的接口会很糟糕。这一节描述了一些SWIG普通的特性,你可以使用它来提高扩展模块的接口。

33.6.1 C++帮助函数

有事你会创建一个函数,他省略了一些功能,例如:

void set_transform(Image *im, double m[4][4]);

python可以得到这个函数,但是成功调用它可是很不容易啊:

>>> a = [

...   [1,0,0,0],

…   [0,1,0,0],

…   [0,0,1,0],

…   [0,0,0,1]]

>>> set_transform(im,a)

Traceback (most recent call last):

File “<stdin>”, line 1, in ?

TypeError: Type error. Expected _p_a_4__double

现在的问题就是,没有一个合适方法来创建并操纵一个合适的double [4][4],为了达到这个目的,你可以自己写一些协助函数。只使用 %inline指令就可以了:

%inline %{

/* Note: double[4][4] is equivalent to a pointer to an array double (*)[4] */

double (*new_mat44())[4] {

return (double (*)[4]) malloc(16*sizeof(double));

}

void free_mat44(double (*x)[4]) {

free(x);

}

void mat44_set(double x[4][4], int i, int j, double v) {

x[i][j] = v;

}

double mat44_get(double x[4][4], int i, int j) {

return x[i][j];

}

%}

在python中,你可以这么用:

>>> a = new_mat44()

>>> mat44_set(a,0,0,1.0)

>>> mat44_set(a,1,1,1.0)

>>> mat44_set(a,2,2,1.0)

>>> set_transform(im,a)

>>>

不得不承认,这不是最好的方法,他虽然可以实现功能, 但是很难去实施。使用typemaps的话这些都不是问题。

33.6.2 增加额外的python代码

如果写C的帮主函数还不够的话,你也可以写python的代码。

void set_transform(Image *im, double x[4][4]);

/* Rewrite the high level interface to set_transform */

%pythoncode %{

def set_transform(im,x):

a = new_mat44()

for i in range(4):

for j in range(4):

mat44_set(a,i,j,x[i][j])

_example.set_transform(im,a)

free_mat44(a)

%}

这个,你就可以这么调用:

>>> a = [

...   [1,0,0,0],

…   [0,1,0,0],

…   [0,0,1,0],

…   [0,0,0,1]]

>>> set_transform(im,a)

>>>

这个,不得不承认,为了包装二维数组参数,整个方案是很特别的。并且,一个python列表和数值python数组不行吗?

33.7 小建议和小技术

虽然SWIG大部分都是自动的,一些包装问题的一些特点需要用户的手工输入。例如在处理字符串,二进制数据和数组时。

33.7.1输入和输出参数

一个问题就是C语言通过指针来传参:

void add(int x, int y, int *result) {

*result = x + y;

}

或者是:

int sub(int *x, int *y) {

return *x-*y;

}

最简单的方式是使用typemaps.i文件:

%module example

%include “typemaps.i”

void add(int, int, int *OUTPUT);

int  sub(int *INPUT, int *INPUT);

python允许你传递简单的值:

>>> a = add(3,4)

>>> print a

7

>>> b = sub(7,4)

>>> print b

3

>>>

注意INPUT参数是允许整数值代替指针来传值,而OUTPUT参数创建一个返回结果。

如果你不想用INPUT和OUTPUT,你可以用%apply指令:

%module example

%include “typemaps.i”

%apply int *OUTPUT { int *result };

%apply int *INPUT  { int *x, int *y};

void add(int x, int y, int *result);

int  sub(int *x, int *y);

如果一个函数不改变一个参数:

void negate(int *x) {

*x = -(*x);

}

那么你就要这么使用:

%include “typemaps.i”

void negate(int *INOUT);

python中,变化的参数通过值返回:

>>> a = negate(3)

>>> print a

-3

>>>

这些特殊的typemap规则大部分被用在了返回参数超过1个结果的和函数上。

33.7.2 简单指针 (important)

如果你只是使用int*double*这样的简单指针,你不必使用typename.i,考虑下cpointer.i吧:

%module example

%include “cpointer.i”

%inline %{

extern void add(int x, int y, int *result);

%}

%pointer_functions(int, intp);

%pointer_functions(type,name),这是个宏,他会创建5个协助函数:create, destroy, copy, assign, and dereference a pointer:

int  *new_intp();

int  *copy_intp(int *x);

void  delete_intp(int *x);

void  intp_assign(int *x, int value);

int   intp_value(int *x);

python中,你可以这么使用:

>>> result = new_intp()

>>> print result

_108fea8_p_int

>>> add(3,4,result)

>>> print intp_value(result)

7

>>>

如果你把%pointer_functions()换成了%pointer_class(type,name),接口就更像类的了:

>>> result = intp()

>>> add(3,4,result)

>>> print result.value()

7

33.7.3 无边界的C数组

C函数有时候希望数组以指针的形式传递:

int sumitems(int *first, int nitems) {

int i, sum = 0;

for (i = 0; i < nitems; i++) {

sum += first[i];

}

return sum;

}

第一个参数应该是一个数组,简单是方式时使用carrays.i库文件:

%include “carrays.i”

%array_class(int, intArray);

%array_class(type, name)是一个宏,为一个无边界数组对象创建了一个包装,         你可以通过int*或者是double*来传值:

>>> a = intArray(10000000)         # Array of 10-million integers

>>> for i in xrange(10000):        # Set some values

…     a[i] = i

>>> sumitems(a,10000)

49995000

>>>

%array_class()包装的数组对象不包装特殊对象数组的指针,事实上,也没有任何边界检查。这个库创建的数组事实上是相当低级的,你不能迭代他们,也不能查询他的长度。不用说,这个方式不会满足所有需求,换句话说,这种低级的方式在创建buffer,二进制包的时候是极其有效的。

33.7.4 字符串处理

如果C字符串有一个参数是char*,那么你可以传递一个python的字符串给他:

// C

void foo(char *s);

# Python

>>> foo(“Hello”)

当python的字符串当做参数传递给C函数时,C函数受到了指向这一块区域的指针。因为python字符串的指针不可变,你要是改变值就是不合法的。事实上,这么做会导致python解释器崩溃。

如果你的程序改变了输入参数,或者是使用它作为返回值,考虑使用cstring.i库。

当函数返回char*, 他被假定为一个NULL终止的字符串,数据被拷被到一个python字符串中,然后返回。

如果你的程序需要处理二进制,你可以使用typemap,扩展你的python字符串到一个 pointer/length参数对:

%apply (char *STRING, int LENGTH) { (char *data, int size) };

int parity(char *data, int size, int initial);

python中你可以这么调用:

>>> parity(“e\x09ffss\x00\x00\x01\nx”, 0)

如果你要返回一个二进制数据,你可以使用cstring.i 或者是cdata.i库。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值