SWIG+c+Python:传递和接收 c 数组

Posted

技术标签:

【中文标题】SWIG+c+Python:传递和接收 c 数组【英文标题】:SWIG+c+Python: Passing and receiving c arrays 【发布时间】:2016-03-25 15:03:00 【问题描述】:

我正在尝试使用 SWIG 和 Python 重用一些旧的 c 代码。 现在我很困惑。我得到的错误可以用一个小例子来演示:

bsp.h:

extern void add(int a[], int b[], int c[]);

bsp.c:

#include "bsp.h" 
void add(int a[], int b[], int c[]) 
 
    c[0] = a[0] + b[0]; 
    c[1] = a[1] + b[1]; 

bsp.i

%module bsp
%
    #include "bsp.h";
%
%include "bsp.h";

setup.py:

#!/usr/bin/env python

from distutils.core import setup, Extension

bsp_module = Extension('_bsp',
    sources = ['bsp_wrap.c', 'bsp.c']
)

setup(name = 'bsp',
    ext_modules = [bsp_module],
    py_modules = ["bsp"]
)

示例 Python 文件“pybsp.py”:

import bsp

a = [1, 1]
b = [1, 1]
c = []

bsp.add(a, b, c)

print(c)

我得到了错误:

Traceback (most recent call last):
    File "pybsp.py", line 31, in <module>
    bsp.add(a, b, c)
TypeError: in method 'add', argument 1 of type 'int []'

现在,我感到困惑的是SWIG Documentation 说: “SWIG 完全支持 C/C++ 指针。此外,SWIG 处理不完整的类型信息也没有问题。”

我也试过添加

%apply int * INPUT  int *a
%apply int * INPUT  int *b
%apply int * OUTPUT  int *c

到我的 .i 文件,这是在这种情况下推荐的,但没有成功。 我的猜测是,我必须在 Python 中创建一个类似对象的指针才能传递,但我不知道它是如何工作的,也希望有一种更简单的方法。

非常感谢您的帮助!

P.S.:你可能猜到了,这是我第一次接触 SWIG,所以很遗憾,我无法从看似相似问题的解决方案中推断出解决方案。

编辑: 我发现对于具有给定维度的数组,如上所述,NumPy 似乎是避免手动包装的好选择。 基本示例给出here. 因此我将函数定义更改为

void add(int* a, int dim_a, int *b, int dim_b, int *c, int dim_c)

现在包装器似乎有机会将 NumPy 数组转换为 C 数组。

i-文件

%module bsp
%
    #define SWIG_FILE_WITH_INIT
    #include "bsp.h"
%

%include "numpy.i"

%init %
    import_array();
%

%apply (int* IN_ARRAY1, int DIM1)(int* a, int dim_a), (int* b, int dim_b)
%apply (int* ARGOUT_ARRAY1, int DIM1)(int* c, int dim_c)

%include "bsp.h"

setup.py

#!/usr/bin/env python

from distutils.core import setup, Extension
import numpy

try:
        numpy_include = numpy.get_include()
except AttributeError:
        numpy_include = numpy.get_numpy_include()

bsp_module = Extension('_bsp',
                       sources=['bsp_wrap.c', 'bsp.c'],
                       include_dirs=[numpy_include]
                       )

setup(name='bsp',
      ext_modules=[bsp_module],
      py_modules=["_bsp"]
      )

最后是 python 脚本,我想使用 int32 来避免来自 NumPy 的类型转换错误 (int64 -> int32)

import bsp
import numpy as np
a = np.array([1, 1], dtype=np.int32)
b = np.array([1, 1], dtype=np.int32)
c = np.array([1, 1], dtype=np.int32)

bsp.add(a, b, c)

print(c)

现在我摆脱了以前的错误,但我有了一个新的:

  File "pybsp.py", line 10, in <module>
    bsp.add(a, b, c)
TypeError: Int dimension expected.  'unknown type' given.

有什么建议吗?

【问题讨论】:

【参考方案1】:

好的,现在我有了。如上面编辑中所写,使用 numpy.i 可以非常舒适地包装数组。我没有看到的是,ARGOUT 数组不想要一个数组作为输入,就像在 C 中一样。只有需要的维度。因此,使用上面的代码,脚本

import bsp
import numpy as np
a = np.array([1, 1], dtype=np.int32)
b = np.array([1, 1], dtype=np.int32)

c = bsp.add(a, b, np.shape(a)[0])

print(c)

给出所需的输出

[2 2]

【讨论】:

【参考方案2】:

Python 列表与 C 数组完全不同。在 C 中,数组的名称是指向包含其元素的连续内存块的指针。 Python 列表是复杂的数据结构,其中内存不连续,简单的地址计算也不成立。因此,您不能指望用于 C 数组的 C 代码与 Python 列表一起使用。

您可以按照此处所述从 C 访问 Python 列表:

http://effbot.org/zone/python-capi-sequences.htm

【讨论】:

谢谢!然而,我的印象是(并且也希望)SWIG 会为我完成这部分工作。当涉及到指针时,自动换行会停止吗?

以上是关于SWIG+c+Python:传递和接收 c 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SWIG/Python 中将结构列表传递给 C

无法确定从 SWIG(不是 ctypes)C 传递给 python 例程的正确参数

如何将复数从 python numpy 传递给 c(目前正在尝试使用 SWIG)

SWIG:将 Python 字符串传递给 void 指针类型的参数

SWIG:将 2d numpy 数组传递给 C 函数 f(double a[])

使用 SWIG 绑定 Python/C++ 模板