从 swig 返回 double * 作为 python 列表

Posted

技术标签:

【中文标题】从 swig 返回 double * 作为 python 列表【英文标题】:return double * from swig as python list 【发布时间】:2012-12-25 19:22:52 【问题描述】:

我有一个 C++ 类,其中一个方法返回一个类似 double * 的数组,这是它的成员变量之一。我试图让它作为 Python 中的列表访问。我将它包装在doubleArray_frompointer 中,然后尝试使用 deepcopy 将其安全地从那里取出,但是当doubleArray 超出范围时我仍然遇到问题,它的内存被清理,然后 C++ 类尝试清理相同的内存(尽管我创建的gist 中没有显示)。

我怀疑我应该使用类型映射来执行此操作。

我要包装的是:

double *foo() 
  double *toReturn = new double[2];
  toReturn[0] = 2;
  toReturn[1] = 4;
  return toReturn;

界面是:

%module returnList
%include "returnList.h"

%include "cpointer.i"
%pointer_functions(double, doubleP)

%include "carrays.i"
%array_class(double, doubleArray);

%
#include "returnList.h"
%

【问题讨论】:

“类型映射通常不是使用 SWIG 的必需部分。”也许我在这里遗漏了一些明显的东西? 【参考方案1】:

您说的正确,可以使用类型映射来避免在 Python 端编写循环。我举了一个例子——它和this other answer非常相似。

%module test

%typemap(out) double *foo %
  $result = PyList_New(2); // use however you know the size here
  for (int i = 0; i < 2; ++i) 
    PyList_SetItem($result, i, PyFloat_FromDouble($1[i]));
  
  delete $1; // Important to avoid a leak since you called new
%

%inline %
double *foo() 
  double *toReturn = new double[2];
  toReturn[0] = 2;
  toReturn[1] = 4;
  return toReturn;

%

这里的类型映射匹配一个名为 foo 的函数返回 double * - 你可以匹配更广泛,但是对于返回 double * 并不意味着你正在返回的函数会有做错事情的风险一个大小为 2 的数组。

有了这个类型图,我就可以运行了:

Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.foo()
[2.0, 4.0]
>>>

您需要像这样手动编写它的原因是因为 SWIG 无法推断您从 foo 返回的数组的长度。它甚至可能因调用而异。

【讨论】:

它会因调用而异,但该长度numRounds 是程序参数之一。也就是说,它一个我们可以在 Python 中访问的变量(大约 200 个)。这是否意味着我应该在 Python 中使用循环?但是,不知何故,我仍然必须在 swig 接口文件或我的 C++ 代码中写入 new,以便 Python 和 C++ 类都不会尝试清理相同的内存。 gist 过于简单化了。我正在返回一个类成员,而不仅仅是从一个函数中。现在已修复,但它没有给出我希望的错误。 我认为这是所问问题的正确答案。但是,我真的应该使用 std::vector 而不是 double * 数组,因为如果包含 std_vector,SWIG 会自动将这些包装为元组,因此它是可迭代的并且长度是已知的。我已经改变了要点以反映这一点。【参考方案2】:

我不是通过返回任何类型的向量或数组对象来“解决”这个问题,而是通过在 Python 中重新创建最初生成数组的(简单)循环。也就是说,我的 C++ 代码只需返回单个双精度值,而我的 Python 代码将组装这个列表。

更明确地说,我有 C++ 方法double *simulation.run(),这会造成麻烦。我创建了一个新的 C++ 方法 double simulation.doRound(),然后通过 SWIG 在 numRounds 迭代的 Python 循环中调用它,每次迭代都执行 outputs.append(simulation.doRound())

但是,我仍然想知道如何通过 SWIG 将 C/C++ double * 数组复制到 Python 列表,因为这似乎是一个基本操作。如果有人可以回答,我会标记为已接受的答案。

【讨论】:

以上是关于从 swig 返回 double * 作为 python 列表的主要内容,如果未能解决你的问题,请参考以下文章

在 SWIG 中将结构从 C++ 函数返回到 Python

带有 SWIG 未知长度数组的 NumPy C 扩展

SWIG:将 2d numpy 数组传递给 C 函数 f(double a[])

如何使用 swig 从`unsigned int *`返回`uint []`

SWIG 如何正确包装 std::vector<double *>

SWIG:如何将复数向量从 C++ 传递到 python