python - 痛饮 python : inject pointer on construction

标签 python c++ swig

我正在尝试了解 SWIG 的工作原理。假设我有这个简单的 Foo-Bar 类:

#include <vector>
class Bar {
 public:
  Bar();
  ~Bar();
  int bar_data;
};

class Foo {
 public:
  // does not take ownership.
  Foo(std::vector<Bar>* b) { b_ = b; }
 private:
  std::vector<Bar>* b_;
};

假设我传递给 Foo 的 vector 很大,所以我不想复制它,只复制指针。在我的 C++ 应用程序中,我将确保注入(inject)到 Foo 的参数(即 b)比我用它创建的 Foo 实例长寿。我如何在 SWIG for python 中执行此操作?我试着写我的类型图(我想我可以使用模板,但这能解决问题吗?我想使用类型图来理解它)。这是 swig 文件:

%module example

%{
#include "example.h"
%}

%typemap(in) std::vector<Bar>* b (std::vector<Bar> temp) {
  PyObject* input = $input;
  if (!PyList_Check(input)) {
    SWIG_exception(SWIG_TypeError, "Input must be a list");
  }
  for (int i = 0; i < PyList_Size(input); ++i) {
    PyObject* obj = PyList_GetItem(input, i);
    Bar* bar;
    if (SWIG_ConvertPtr(obj, (void**)&bar, $descriptor(Bar), 1) == -1) {
      SWIG_exception(SWIG_TypeError, "Input list element was not a Bar");
    }
    temp.push_back(*bar);
  }
  $1 = &temp;
}

然而,当我查看生成的代码时,我看到了可能出现问题的地方:

SWIGINTERN PyObject *_wrap_new_Foo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  std::vector< Bar > *arg1 = (std::vector< Bar > *) 0 ;
  std::vector< Bar > temp1 ;
  PyObject * obj0 = 0 ;
  Foo *result = 0 ;

  if (!PyArg_ParseTuple(args,(char *)"O:new_Foo",&obj0)) SWIG_fail;
  {
    PyObject* input = obj0;
    if (!PyList_Check(input)) {
      SWIG_exception(SWIG_TypeError, "Input must be a list");
    }
    for (int i = 0; i < PyList_Size(input); ++i) {
      PyObject* obj = PyList_GetItem(input, i);
      Bar* bar;
      if (SWIG_ConvertPtr(obj, (void**)&bar, SWIGTYPE_Bar, 1) == -1) {
        SWIG_exception(SWIG_TypeError, "Input list element was not a Bar");
      }
      temp1.push_back(*bar);
    }
    arg1 = &temp1;
  }
  result = (Foo *)new Foo(arg1);
  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Foo, SWIG_POINTER_NEW |  0 );
  return resultobj;
fail:
  return NULL;
}

也就是说,Foo* 类型的结果是使用指向局部变量 (arg1 = &temp1) 的指针创建的。这看起来是错误的。无论如何我可以正确地做到这一点吗?我是 SWIG 的新手,因此指向解决此问题的文档的指针也很有用。

我是否可以在不更改 C++ 接口(interface)的情况下至少模拟构造函数参数的拷贝?也就是说,创建 python Foo(my_list_of_bars)将表现得好像要包装的 c++ 代码是 Foo(std::vector<Bar> b) .我认为我可以在类型映射中做的一件事是使用临时指针,从 python 列表中复制值,然后将其传递给 Foo。所以第一行会变成:

%typemap(in) std::vector<Bar>* b (std::vector<Bar> *temp) {
  temp = new std::vector<Bar>;

但是我怎样才能清理生成的代码中的临时文件?

最佳答案

如您所见,尝试传递 Python 列表只会创建一个临时的 std::vector 来调用 Foo 构造函数。要创建一个可以管理其生命周期的永久类,请将 std::vector 类公开给 Python,构建一个,然后传递它:

例子:

%module x

%{
#include <vector>
%}

%inline %{
    struct Bar {
        int data;
    };
%}

%include <std_vector.i>
%template(BarVec) std::vector<Bar>;

%inline %{
class Foo {
    std::vector<Bar>* b_;
public:
    // does not take ownership.
    Foo(std::vector<Bar>* b) { b_ = b; }
    std::vector<Bar>* get() { return b_; }  # Added to inspect that it worked.
};
%}

演示:

>>> import x
>>> v = x.BarVec()
>>> for i in range(10):
...  b = x.Bar()
...  b.data = i
...  v.push_back(b)
...
>>> f = x.Foo(v)
>>> v2 = f.get()
>>> v[5].data
5
>>> v2[5].data
5
>>> v[5].data = 7   # Change original vector data passed in.
>>> v2[5].data      # vector from Foo also changes.
7
>>> del v           # delete the original vector
>>> v2              # proxy is still there
<x.BarVec; proxy of <Swig Object of type 'std::vector< Bar > *' at 0x00000000027CA4C0> >
>>> v2[0]           # <CRASH>

关于python - 痛饮 python : inject pointer on construction,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22364395/

相关文章:

python - 如何更正此错误 PyPDF2.utils.PdfReadError : Cannot read an empty file

python - 参数 'src' 的预期 cv::UMat

c++ - 实现一个模板函数,将两个类作为模板参数,如果被继承则返回true

GTest TYPED_TEST 的 C++ 多个参数

java - SWIG 在为 Java 包装 C++ 时显示错误

由于预处理器指令导致的 SWIG 错误

java - SWIG:使用导入的外部类型

python - 无法使用anaconda安装ggp​​lot

python如何检查一个字符串是否是一个字符串列表的元素

c++ - 乘以存储在一维数组算法中的上三角矩阵