简体   繁体   中英

SWIG Converting a vector of vectors to a list of lists for Python, 'x' was not declared in this scope

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM