SWIG C到Python整数数组

11 投票
3 回答
10667 浏览
提问于 2025-04-16 16:22

我正在尝试通过swig从Python访问一个C语言的函数,函数的格式如下:

int cosetCoding(int writtenDataIn, int newData, const int memoryCells, int *cellFailure, int failedCell);

Swig成功创建了.so文件,我可以在Python中导入它,但当我尝试用以下方式访问它时:

 cosetCoding.cosetCoding(10,11,8,[0,0,0,0,0,0,0,0],0)

我收到了以下错误信息:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: in method 'cosetCoding', argument 4 of type 'int *'

这个指针应该是一个整数数组,大小由memoryCells定义。

3 个回答

2

为了让这个工作,你需要构建一个 c_int 类型的数组:

arr = (ctypes.c_int * 8)(0, 0, 0, 0, 0, 0, 0, 0)
cosetCoding.cosetCoding(10, 11, 8, arr, 0)

更新:我添加了一个更完整的例子。我在 Python 2.6 下使用 ctypes 版本 1.1.0;也许我们做的事情有点不同?

也许可以试试传递 ctypes.byref(arr) 呢?

cosetCoding.cosetCoding(10, 11, 8, ctypes.byref(arr), 0)

这是我测试的内容:

共享对象: cosetCoding.c

#include <stdio.h>
int cosetCoding(int writtenDataIn, int newData, const int memoryCells, int *cellFailure, int failedCell)
{
     printf("cellFailure: %d %d\n", cellFailure[0], cellFailure[1]);
}

编译:

% gcc -shared -fPIC -o cosetCoding.so cosetCoding.c

Python 脚本: test_coset.py

import ctypes
cosetCoding = ctypes.cdll.LoadLibrary('./cosetCoding.so')
arr = (ctypes.c_int * 8)(1, 2, 3, 4, 5, 6, 7, 8)
cosetCoding.cosetCoding(10, 11, 8, arr, 0)

输出:

% python test_coset.py
cellFailure: 1 2
8

马克说得对,你确实需要一个类型映射(typemap)。不过,如果你使用 numpy.i(http://docs.scipy.org/doc/numpy/reference/swig.interface-file.html),就不需要手动编写类型映射,因为它已经定义好了将C语言数据转换成NumPy数组以及反向转换所需的类型映射。

在你的情况下(假设 cellFailure 是一个输入数组),你需要使用

%apply (int DIM1, int* IN_ARRAY1) {(int memoryCells, int *cellFailure)}

注意(正如马克已经提到的),这会方便地将C语言中的这两个参数合并成一个Python数组参数,所以不需要单独传递数组的长度。你的调用看起来会像这样:

from numpy import asarray
cosetCoding.cosetCoding(10,11,asarray([0,0,0,0,0,0,0,0]),0)
14

如果可以的话,建议使用ctypes,因为它更简单。不过,既然你提到了SWIG,你需要一个类型映射来描述如何处理int*。SWIG并不知道指向了多少个整数。下面的内容是从SWIG文档中的一个例子修改而来的,关于多参数类型映射

%typemap(in) (const int memoryCells, int *cellFailure) {
  int i;
  if (!PyList_Check($input)) {
    PyErr_SetString(PyExc_ValueError, "Expecting a list");
    return NULL;
  }
  $1 = PyList_Size($input);
  $2 = (int *) malloc(($1)*sizeof(int));
  for (i = 0; i < $1; i++) {
    PyObject *s = PyList_GetItem($input,i);
    if (!PyInt_Check(s)) {
        free($2);
        PyErr_SetString(PyExc_ValueError, "List items must be integers");
        return NULL;
    }
    $2[i] = PyInt_AsLong(s);
  }
}

%typemap(freearg) (const int memoryCells, int *cellFailure) {
   if ($2) free($2);
}

注意,使用这个定义时,从Python调用时要省略memoryCells这个参数,只需传入一个数组,比如[1,2,3,4]cellFailure。类型映射会自动生成memoryCells这个参数。

另外,如果你需要的话,我可以提供一个完整的示例(适用于Windows)。

撰写回答