包装一个带有指针数组的函数

时间:2012-04-26 06:31:48

标签: python c interface swig

我定义了struct a { }。我的C函数通过引用获取struct a数组,然后填充其中的数据。所以它接受一个参数struct a **。我想使用SWIG接口从Python调用此函数。有没有办法做到这一点?

1 个答案:

答案 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
  • 如果您不想在Python界面上指定列表的大小(例如,已知或以其他方式修复),您可以将numinputs=1更改为0并相应地设置len在in typemap。
  • 如果您的函数采用列表和指定其长度的整数而不是使用NULL终止列表,则可以将其扩展为multi-argument typemap

编译/运行此示例所需的所有代码都包含在此答案中,但如果您想在一个方便的tarball中使用它,我还会将其on my site。即使该链接应该破坏,答案仍然有效。