I am trying to understand how SWIG works. Say I have this simple Foo-Bar classes:
#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_;
};
Say the vector I am passing to Foo is big, so I do not want to copy it, just copy the pointer. In my c++ application I will make sure that the argument injected to Foo (ie, b) outlives the Foo instance I created with it. How do I do this in SWIG for python? I tried to write my typemap (I guess I could use template, but would that solve the problem? I would like to understand it using typemaps). This is the swig file:
%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;
}
When I look at the generated code, however, I see where a problem might arise:
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;
}
that is, result, of type Foo*, is created with a pointer to a local variable (arg1 = &temp1
). That looks like wrong. Is there anyway I could do this properly? I am very new to SWIG, so a pointer to documentation addressing this would be also useful.
Could I at least emulate a copy of the constructor argument without changing the c++ interface? That is, creating the python Foo(my_list_of_bars)
would behave as if the c++ code to wrap were Foo(std::vector<Bar> b)
. One thing I thought I could do, in the typemap, is to use a temp pointer, copying the values from the python list and then pass it to Foo. So the first lines would change as:
%typemap(in) std::vector<Bar>* b (std::vector<Bar> *temp) {
temp = new std::vector<Bar>;
but then how could I clean up temp in the generated code?