我有一个struct a { }
定义。我的 C 函数struct a
通过引用获取一个数组,然后在其中填充数据。所以它接受一个论点struct a **
。我想使用 SWIG 接口从 Python 调用此函数。有没有办法做到这一点?
1 回答
您可以使用 SWIG 和 Python 完成此操作。我已经设置了以下 test.h 文件来演示:
struct a {
int val;
};
static void populate(struct a **list) {
int count = 0;
// Terminate when NULL entry found
while (*list) {
(*list)->val = count++;
++list;
}
}
populate 函数采用struct a**
您所描述的 a ,假设列表以 NULL 结尾并对列表的每个元素执行某些操作。
我选择将它暴露给 Python 的方式是作为一个函数,它接受一个整数,即要使用的列表的大小,然后返回结果列表,因为这是映射“通过返回的语义”的最佳方式论据”在我看来成立。
我设置了一个基本模块文件:
%module test
%{
#include "test.h"
%}
然后添加了一个类型映射,它根据指定给 Python 函数的大小准备一个输入列表:
%typemap(in,numinputs=1) struct a ** (int len=0) {
len = (int)PyInt_AsLong($input);
$1 = malloc(sizeof(struct a*)*(len+1));
$1[len] = NULL;
for (int i = 0; i < len; ++i) {
$1[i] = malloc(sizeof(struct a));
}
}
基本上,它将参数视为整数,为指针数组分配内存,然后为元素本身指向的对象分配一些内存。(我单独分配它们以便 SWIG/Python 可以单独处理每个项目的引用计数,这比为所有元素分配一个块更简单)
编写完该类型映射后,我添加了另一个类型映射,它负责获取调用函数的结果并将其转换回 PyList:
%typemap(argout) struct a ** {
// Push into PyList for return
$result = PyList_New(len$argnum);
for (int i = 0; i < len$argnum; ++i) {
PyObject *element = SWIG_NewPointerObj(SWIG_as_voidptr($1[i]), SWIGTYPE_p_a, SWIG_POINTER_OWN);
PyList_SET_ITEM($result, i, element);
}
}
它利用了len
我们在 in 类型映射中创建的变量,尽管由于它是 NULL 终止的,我们可以再次计算长度。struct a
然后,我们为传递给populate
函数的每个对象使用一个包装对象(由 SWIG/Python 拥有)填充 Python 列表中的每个项目。请注意,如果类型名称不同,您将需要更改SWIGTYPE_p_a
为适当的SWIGTYPE
. (这些可以从生成的包装源中找到)。
最后,我们需要为 C 端的列表取消分配内存,方法是:
%typemap(freearg) struct a ** {
free($1);
}
然后让 SWIG 使用这些类型映射来包装头文件本身:
%include "test.h"
我编译了这个:
痛饮 -python -Wall test.i gcc -Wall -Wextra test_wrap.c -I/usr/include/python2.6 -o _test.so -std=c99 -shared
然后运行以下 Python 进行检查:
import test
r=test.populate(100)
print r[0].val
这个例子有几点需要注意:
- 没有错误检查,例如
test.populate("Hello world")
会做坏事,PyList_New
可能会失败,如果确实失败了,我们需要释放单个元素,而不仅仅是列表 - 如果内存所有权语义不同,则需要
SWIG_POINTER_OWN
适当更改 - 如果您不想在 Python 接口上指定列表的大小(例如,它是已知的或以其他方式固定),您可以将 更改
numinputs=1
为 0 并len
在 in 类型映射中进行相应设置。 - 如果您的函数采用列表和指定其长度的整数而不是使用以 NULL 结尾的列表,则可以将其扩展为多参数类型映射。
编译/运行此示例所需的所有代码都包含在此答案中,但如果您希望将其放在一个方便的 tarball 中,我也将其放在我的网站上。即使该链接应该断开,答案仍然有效。