Home > Blockchain >  SWIG Converting a vector of vectors to a list of lists for Python, 'x' was not declared in
SWIG Converting a vector of vectors to a list of lists for Python, 'x' was not declared in

Time:08-20

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.

  • Related