문제

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?

도움이 되었습니까?

해결책

As you've found, trying to pass a Python list creates only a temporary std::vector to call the Foo constructor. To create a permanent one that you can manage the lifetime of, expose the std::vector class to Python, build one up, and pass it instead:

Example:

%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.
};
%}

Demo:

>>> 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>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top