1

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

4

1 回答 1

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**您所描述的 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 中,我也将其放在我的网站上。即使该链接应该断开,答案仍然有效。

于 2012-04-29T11:53:35.990 回答