SWIG 将向量的向量转换为 Python 的列表列表,在此范围内未声明"x"



我有一个简短的c++函数,当给定的输入a、b是行和列时,它会填充一个向量向量,如下所示vec_of_vc.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;
}

其头文件vec_of_vc.h

#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
#include <vector>
#include <functional>
std::vector<std::vector<int>> f(int a,int b); 
#endif

从这里开始,我想从python调用这段代码,所以我尝试用SWIG包装它。这是我一直在使用的接口文件。我从这篇文章中获得了灵感。

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"

这是生成文件:

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

当我调用这个时,我得到一个错误:

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

然而,当我尝试在接口文件中手动填充它时,没有for循环,类似于以下内容:

%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);
%}

它返回一个列表列表。

另一件事是,如果我将%typemap(out(更改为%typemap(argout(,我会得到一个元组,但它不会出错。

如果不明显的话,我对c++或swig不是很熟练,但我正在努力找到解决方法,但我一辈子都找不到。

虽然错误消息与显示的代码或标题中的错误不一致,但您还是很接近。显示的代码为for(int x = 0...),但错误消息为for(x = 0...)。在显示的代码中,您在%typemap(out)实现中缺少下面标记的大括号:

%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);
%}

修复后输出:

>>> 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]]

请注意,您的代码使用%typemap(argout),因为您声明的vector<vector<int>>模板有一个默认的%typemap(out),用于生成元组元组,而%typemap(argout)根本没有使用。如果元组的元组对您来说是可以的,那么您不需要输出列表列表的%typemap(out)

最新更新