我想了解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的向量很大,所以我不想复制它,只需复制指针即可。在我的c ++应用程序中,我将确保注入Foo的参数(即b)比我用它创建的Foo实例更长。我如何在SWIG for python中执行此操作?我试着编写我的typemap(我想我可以使用模板,但是会解决问题吗?我想用类型图来理解它)。这是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 ++接口的情况下模拟构造函数参数的副本?也就是说,创建python Foo(my_list_of_bars)
的行为就像要包装的c ++代码Foo(std::vector<Bar> b)
一样。我认为我可以在typemap中做的一件事是使用临时指针,复制python列表中的值,然后将其传递给Foo。所以第一行会改变为:
%typemap(in) std::vector<Bar>* b (std::vector<Bar> *temp) {
temp = new std::vector<Bar>;
但是我怎么能在生成的代码中清理temp?
答案 0 :(得分:1)
正如您所发现的那样,尝试传递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>