Return vector<pair<int,int>> & from c++ method to python list of tuples using swig typemap

c++, python, swig, vector

Solution

A custom typemap is not necessary for this case. SWIG has built-in support for vector and pair templates, but you have to declare the pair template as well as the vector template:

%module x

%include <std_pair.i>
%include <std_vector.i>
%include <std_string.i>
%template() std::pair<int,int>;
%template(PairVector) std::vector<std::pair<int,int> >;

%{
#include "MyClass.h"
%}

%include "MyClass.h"

Example:

>>> import x
>>> a=x.PairVector(((1,2),(3,4),(5,6)))
>>> b=x.MyClass(a)
>>> b.GetMyVector()
((1, 2), (3, 4), (5, 6))

But note because your class is written to hold a reference to the passed-in vector and not a copy, you have to hold a reference to it for the lifetime of MyClass. For example:

>>> del a
>>> b.GetMyVector()
*Undefined Behavior (crash, empty vector, etc.)*

Problem

I'm having a lot of troubles trying to wrap a c++ method that returns a constant reference to a vector of pairs to a Python list of tuples using `%typemap(out)`. I currently have something like this: myclass.h: ``` #inlcude <vector> using std::vector; class MyClass { private: const vector<pair<int,int>> & _myvector; public: MyClass(const vector<pair<int,int>> & myvector ); const vector<pair<int,int>> & GetMyVector() const; } ``` myclass.cpp: ``` #include "myclass.h" MyClass::MyClass(const vector<pair<int,int>> & myvector): _myvector(myvector){}; const vector<pair<int,int>> & MyClass::GetMyVector() const { return _myvector; }; ``` myclass.i: (checking ommited) ``` %module MyClass %include "std_vector.i" namespace std { %template(vector_pair_int) vector<pair<int,int>>; } %typemap(in) (const vector_pair_int &){ $1 = new vector<pair<int,int>>; int size = PyList_Size($input); for (int i=0; i<size; i++){ PyObject *o = PyList_GetItem($input,i); PyObject *o1 = PyList_GetItem(o,0); PyObject *o2 = PyList_GetItem(o,1); $1->push_back(make_pair(PyInt_AsLong(o1),PyInt_AsLong(o2))) } } %typemap(out) (const vector_pair_int &) { $result = PyList_New($1.size()); vector<pair<int,int>>:: const_iterator it; int count=0; for (it= $1.begin(); it!= $1.end(); ++it){ PyObject * tup = PyTuple_Pack(2, it->first,it->second); PyList_SET_ITEM($result,count,tup); count++; } } ``` Ok so the first thing that I dont quite understand is that the previous code for the typemap (out) doesnt compile because it tells me that $1 is a pointer to the container not a reference. When I change the use of $1 as a pointer, it compiles but it doesnt work. Second, in the case that compiles, the typemap(in) works (the c++ container is correctly filled), but when I try to retrieve the container from python I get garbage. When I pass to the constructor something like MyClass([(1,2)]), and then I use GetMyVector() it returns a python list but of size 3 and with garbage on the tuples... what I'm doing wrong???

Original source