Python/SWIG:输出一个数组

7
我可以帮您翻译成中文。这是一个关于IT技术的问题,您想要将一个C函数包装使用SWIG用于Python时输出一个值数组。我尝试使用以下类型映射来实现。
伪代码:
int oldmain() {
float *output = {0,1};
return output;
}

类型映射:

%typemap(out) float* { 
   int i; 
  $result = PyList_New($1_dim0); 
   for (i = 0; i < $1_dim0; i++) { 
 PyObject *o = PyFloat_FromDouble((double) $1[i]); 
 PyList_SetItem($result,i,o); 
 } 
} 

我的代码编译成功,但是当我运行访问这个函数时,它卡住了(没有更多的调试方式)。

请问我哪里有问题?

谢谢。


你能分享你的Makefile或类似文件吗?除非你需要处理比Python更多的语言,否则你可能会发现Cython比SWIG更容易。 - user1277476
谢谢,Oleksiy的回答解决了我的问题,但正如你所说,Cython似乎更容易,我正在探索它。再次感谢。 - SEU
2个回答

7
添加另一个输出参数来指示数组的大小是允许长度变化的最简单方法:
%module test

%include <stdint.i>

%typemap(in,numinputs=0,noblock=1) size_t *len  {
  size_t templen;
  $1 = &templen;
}

%typemap(out) float* oldmain {
  int i;
  $result = PyList_New(templen);
  for (i = 0; i < templen; i++) {
    PyObject *o = PyFloat_FromDouble((double)$1[i]);
    PyList_SetItem($result,i,o);
  }
}

%inline %{
float *oldmain(size_t *len) {
  static float output[] = {0.f, 1.f, 2, 3, 4};
  *len = sizeof output/sizeof *output;
  return output;
}
%}

这是从这个答案修改而来的,添加了size_t *len参数,可以在运行时返回数组的长度。然而,typemap完全隐藏了Python包装器的输出,并且使用它来控制返回列表的长度,而不是使用固定大小的%typemap(out)


感谢您的详细回复。我会尝试这个方法。另外一个问题:如果我在我的C代码中有一个变量或者一个#define常量,它表示我的输出数组长度,那么我能否使用它来代替你示例中的“templen”呢? - SEU
@seu - 最简单的方法就是将其放入调用PyList_Newfor循环的输出类型映射中。 - Flexo
似乎不起作用:我尝试在我的C文件中定义一个常量,但是我得到了以下错误example_wrap.c:2980: error: ‘ARRLENGTH’ undeclared (first use in this function)。我的意图是不再声明它,但以某种方式能够访问这个变量/常量。 - SEU
@SEU - 在类型映射之前,您需要执行%{ #include "whatever.h" %}以使ARRLENGTH的定义在生成的包装器中可见。 - Flexo

6
这应该能帮助您开始:
/* example.c */

float * oldmain() {
    static float output[] = {0.,1.};
    return output;
}

这里返回了一个指针,而swig并不知道它的大小。简单的$1_dim0无法工作,因此您需要硬编码或进行其他操作。类似这样:

/* example.i */
%module example
%{
 /* Put header files here or function declarations like below */
  extern float * oldmain();
%}

%typemap(out) float* oldmain {
  int i;
  //$1, $1_dim0, $1_dim1
  $result = PyList_New(2);
  for (i = 0; i < 2; i++) {
    PyObject *o = PyFloat_FromDouble((double) $1[i]);
    PyList_SetItem($result,i,o);
  }
}

%include "example.c"

然后在Python中,您应该得到:
>> import example
>> example.oldmain()
[0.0, 1.0]

在添加类型映射时,您可能会发现-debug-tmsearch非常方便,即

swig -python -debug-tmsearch example.i

应明确表明您的类型映射用于查找适合float *oldmain的'out'类型映射。此外,如果您只想访问C全局变量数组,则可以使用varout的类型映射而不仅仅是out来执行相同的技巧。


嗨,非常感谢。这个很好用。我正在尝试将其推广到任何长度的数组,所以我使用$1_dim0代替“2”,但我遇到了这个问题(也在文档中找到http://www.swig.org/Doc1.3/Typemaps.html#Typemaps_nn39)。 example_wrap.c:2979:错误:在此函数中首次使用未声明的‘result_dim0’ - SEU
@seu - 你不能这样做,因为数组的大小未知。返回类型是 float*,而不是 $1_dim0 所需的 float[2] - Flexo
@Flexo。谢谢。我尝试使用文档中的"%typemap(out) float [ANY] {",并且编译通过了,但是在Python中我得到的返回值是<Swig Object of type 'float *' at 0x9175470>。基本上,我想要返回一个长度可变的数组。有没有办法做到这一点,或者我总是需要知道数组的长度? - SEU
对于-debug-tmsearch,点赞;我曾经遇到过类似的问题,调试输出帮助我发现我实际上想写一个 'argout' 类型映射而不是 'out' 类型映射。 - user7610

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接