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.
CodePudding user response:
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.