swig处理C指针参数传递

用Python调用C模块容易发生的就是参数类型不一致的问题,比如,C函数接收传入指针,python端怎么办?这里使用swig作为一种备选方案,处理方式不一定明智简洁,旨在说明可行性。

  • 方案一:自我定义空间分配,释放,访问,修改等函数,用swig封装
  • 方案二:使用swig内置carrays.i,这里用FFT举个栗子

使用方法,直接在.i文件中声明carrays.i

1
2
3
4
5
6
7
8
9
10
# fft.i
%module fft
%{
#define SWIG_FILE_WITH_INIT
#include "fft.h"
%}

%include "fft.h"
%include "carrays.i"
%array_functions(float, floatArray);

其中array_functions(type, name)会创建一下四个函数

1
2
3
4
type *new_name(int nelements)   # 申请内存
type *delete_name(type *ary) # 释放内存
type name_getitem(type *ary, int index) # 访问
void name_setitem(type *ary, int index, type value) # 赋值

定义setup.py如下,也可以手动编译动态库

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python
from distutils.core import setup, Extension

example_module = Extension('_fft',
sources=['fft.cpp', 'fft_wrap.cxx',], )

setup (name = 'fft',
version = '0.1',
author = "wujian",
description = """FFT implement by C""",
ext_modules = [example_module],
py_modules = ["fft"],
)

执行(--inplace使得生成的动态库在当前目录下)

1
2
swig -python -c++ fft.i
python setup.py build_ext --inplace

编写测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import fft

R = fft.new_floatArray(16)
I = fft.new_floatArray(16)

for i in range(16):
fft.floatArray_setitem(R, i, 0)
fft.floatArray_setitem(I, i, 0)

for i in range(4):
fft.floatArray_setitem(R, i, 1)

fft.ComplexFFT(R, I, 16, 0)
fft.ComplexFFT(R, I, 16, 1)

for i in range(16):
print "[%10f %10f]" %(fft.floatArray_getitem(R, i), fft.floatArray_getitem(I, i))

fft.delete_floatArray(R)
fft.delete_floatArray(I)

测试结果

[  1.000000  -0.000000]
[  1.000000  -0.000000]
[  1.000000   0.000000]
[  1.000000  -0.000000]
[  0.000000   0.000000]
[ -0.000000   0.000000]
[  0.000000   0.000000]
[ -0.000000   0.000000]
           ...

附FFT源码实现

1
2
3
4
5
6
// FFT.h
#include <string.h>
#include <math.h>

const float PI = 3.14159265;
void ComplexFFT(float *R, float *I, int N, int invert);
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include "fft.h"

void ComplexFFT(float *R, float *I, int N, int invert)
{
int n, nn, i, j, m, cnt, inc, k;
float tmpR, tmpI, WR, WI, Ri, Ii, Rj, Ij;

// 原版本R[0]是数组大小,从R[1]开始是数据区域
R--, I--;

n = N, nn = n >> 1;
for(j = 0, i = 0; i < n - 1; i++)
{
if(i < j)
{
tmpR = R[j + 1], tmpI = I[j + 1];
R[j + 1] = R[i + 1], I[j + 1] = I[i + 1];
R[i + 1] = tmpR, I[i + 1] = tmpI;
}
m = n >> 1;
while(j >= m)
{
j = j - m;
m = m >> 1;
}
j = j + m;
}

m = 1;
// 1, 2, 4 级
while(m < n)
{
/*
m = 1: [1, 2], [3, 4], [5, 6], [7, 8] 4
m = 2: [1, 3], [2, 4], [5, 7], [6, 8] 2
m = 4: [1, 5], [2, 6], [3, 7], [4, 8] 1
*/
//printf("M = %d\n", m);
cnt = 0, inc = n / (m << 1);
// inc: 4 2 1
// m : 1 2 4
// W递增inc
while(cnt < inc)
{
// m = 1: 1 3 5 7
// m = 2: 1 5
// m = 4: 1
i = cnt * m * 2 + 1;
// W[0, n]: inc
// 计算m次 迭代inc次
for(int t = 0; t < m; t++, i++)
{
j = i + m;
k = t * inc;
// printf("[%3d, %3d] W[%3d, %3d]\n", i, j, k, nn);
k == 0 ? WR = 1.0, WI = 0.0: WR = cos(PI * k / nn), WI = -sin(PI * k / nn);
if(invert) WI = - WI;
//(R[i], I[i]) = (Ri, Ii) + W * (Rj, Ij)
//(R[j], I[j]) = (Ri, Ii) - W * (Rj, Ij)
Rj = R[j], Ij = I[j], Ri = R[i], Ii = I[i];
R[i] = Ri + WR * Rj - WI * Ij, I[i] = Ii + WR * Ij + WI * Rj;
R[j] = Ri - WR * Rj + WI * Ij, I[j] = Ii - WR * Ij - WI * Rj;
}
cnt++;
}
m = m << 1;
}

if (invert)
for (i = 1; i <= n; i++)
R[i] = R[i] / n, I[i] = I[i] / n;
}