I have a short c++ function that populates a vector of vectors when given inputs a,b which are rows and columns, like this vec_of_vec.cpp
#include <iostream>
#include "vec_of_vec.h"
#include <vector>
std::vector<std::vector<int>> f(int a, int b) {
std::vector<std::vector<int>> mat;
for (int i = 0; i < a; i++)
{
// construct a vector of int
std::vector<int> v;
for (int j = 0; j < b; j++) {
v.push_back(j+i);
}
// push back above one-dimensional vector
mat.push_back(v);
}
return mat;
}
its header file vec_of_vec.h
#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
#include <vector>
#include <functional>
std::vector<std::vector<int>> f(int a,int b);
#endif
From here I want to call this code from python so I try and wrap it with SWIG. This is the interface file I've been using. I've taken inspiration from this post.
vec_of_vec.i
%module vec_of_vec
#define SWIGPYTHON_BUILTIN
%{
#include "vec_of_vec.h"
#include <vector>
%}
%include <std_vector.i>
%template(VectorOfInt) std::vector<int>;
%template(VectorOfStructVector) std::vector<std::vector<int> >;
%typemap(out) std::vector<std::vector<int>> (PyObject* _inner,PyObject* _outer) %{
// Allocate a PyList object of the requested size.
_outer = PyList_New($1.size());
// Populate the PyList. PyLong_FromLong converts a C++ "long" to a
// Python PyLong object.
for(int x = 0; x < $1.size(); x++)
_inner = PyList_New($1[x].size());
for(int y = 0; y < $1[x].size(); y++)
PyList_SetItem(_inner,y,PyLong_FromLong($1[x][y]));
PyList_SetItem(_outer,x,_inner);
$result = SWIG_Python_AppendOutput($result,_outer);
%}
%include "vec_of_vec.h"
This is the makefile:
all:
rm -f *.so *.o *_wrap.* *.pyc *.gch vec_of_vec.py
swig -c++ -python vec_of_vec.i
g++ -fpic -c vec_of_vec_wrap.cxx vec_of_vec.h vec_of_vec.cpp -I/usr/include/python3.8
g++ -shared vec_of_vec_wrap.o vec_of_vec.o -o _vec_of_vec.so
When I call this I get an error:
vec_of_vec_wrap.cxx: In function ‘PyObject* _wrap_f(PyObject*, PyObject*)’:
vec_of_vec_wrap.cxx:9412:3: error: expected initializer before ‘for’
9412 | for(x = 0; x < (&result)->size(); x++)
| ^~~
vec_of_vec_wrap.cxx:9412:40: error: expected ‘;’ before ‘)’ token
9412 | for(x = 0; x < (&result)->size(); x++)
| ^
| ;
vec_of_vec_wrap.cxx:9414:7: error: ‘y’ was not declared in this scope
9414 | for(y = 0; y < result[x].size(); y++)
| ^
make: *** [makefile:4: all] Error 1
However when I try and populate it manually, in the interface file, without a for loop, something similar to this:
%typemap(out) std::vector<std::vector<int>> (PyObject* _inner,PyObject* _outer) %{
// Allocate a PyList object of the requested size.
_outer = PyList_New($1.size());
_inner = PyList_New($1[0].size());
PyList_SetItem(_inner,0,PyLong_FromLong($1[0][0]));
PyList_SetItem(_inner,1,PyLong_FromLong($1[0][1]));
PyList_SetItem(_outer,0,_inner);
...
$result = SWIG_Python_AppendOutput($result,_outer);
%}
it returns a list of lists.
Another thing is that if i change %typemap(out) to %typemap(argout), I get a tuple of tuples back, but it works without errors.
If it's not obvious, I'm not very proficient with c++ or swig but I'm trying to find my way around it but can't figure it out for the life of me.
You were close, although the error message doesn't agree with the code shown or the error in the title. Code shown is for(int x = 0...)
but error message is for(x = 0...)
. In the code shown you are missing the marked curly braces below in the %typemap(out)
implementation:
%typemap(out) std::vector<std::vector<int>> (PyObject* _inner,PyObject* _outer) %{
// Allocate a PyList object of the requested size.
_outer = PyList_New($1.size());
// Populate the PyList. PyLong_FromLong converts a C++ "long" to a
// Python PyLong object.
for(int x = 0; x < $1.size(); x++) { // <<<<<<< MISSING
_inner = PyList_New($1[x].size());
for(int y = 0; y < $1[x].size(); y++)
PyList_SetItem(_inner,y,PyLong_FromLong($1[x][y]));
PyList_SetItem(_outer,x,_inner);
} // <<<<<<< MISSING
$result = SWIG_Python_AppendOutput($result,_outer);
%}
Output after fix:
>>> import vec_of_vec as vv
>>> vv.f(3,5)
[[0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]
Note that your code worked with %typemap(argout)
because there is a default %typemap(out)
for the vector<vector<int>>
template you declared that generates a tuple of tuples, and %typemap(argout)
simply wasn't used. If a tuple of tuples is OK for you, then you don't need the %typemap(out)
that outputs a list of lists.