我定义了struct a { }
。我的C函数通过引用获取struct a
数组,然后填充其中的数据。所以它接受一个参数struct a **
。我想使用SWIG接口从Python调用此函数。有没有办法做到这一点?
答案 0 :(得分:4)
您可以使用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**
,假设列表以NULL结尾并对列表的每个元素执行某些操作。
我选择将它公开给Python的方式是一个函数,它接受一个整数,即要使用的列表大小,然后返回结果列表,因为这是映射语义的最好方法。通过在我看来设置的参数返回。
我设置了一个基本模块文件:
%module test
%{
#include "test.h"
%}
然后添加了一个typemap,它根据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);
}
}
它利用了我们在in typemap中创建的len
变量,虽然因为它是NULL终止,我们可以再次计算长度。然后,我们使用包装对象(由SWIG / Python拥有)为我们传递到struct a
函数的每个populate
填充Python列表中的每个项目。请注意,如果类型名称不同,则需要将SWIGTYPE_p_a
更改为相应的SWIGTYPE
。 (这些可以从生成的包装器源中找到。)
最后,我们需要在C侧取消分配我们对列表的内存,使用:
%typemap(freearg) struct a ** {
free($1);
}
然后让SWIG使用这些类型图包装头文件:
%include "test.h"
我编译了这个:
swig -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
numinputs=1
更改为0并相应地设置len
在in typemap。编译/运行此示例所需的所有代码都包含在此答案中,但如果您想在一个方便的tarball中使用它,我还会将其on my site。即使该链接应该破坏,答案仍然有效。