SWIG类型映射配置异常:调用Point::myfunc未获预期修改后的列表
问题描述
现有C++类Point包含如下方法:
int Point::myfunc(int a, std::vector<float> *b) { for (short int i = 0; i <= 10; i = i + 1) b->push_back(static_cast<float>(a * i)); return 0; }
编写的SWIG接口文件如下:
%module Point %{ #include "Point.h" %} %include <std_string.i> // Define typemaps for std::vector<float>* %typemap(in) std::vector<float>* (std::vector<float> tmp) { $1 = &tmp; } %typemap(argout) std::vector<float>* { // Nothing needed here; the vector is modified in-place. } %include "Point.h"
通过命令swig -c++ -python -Wall Point.i生成Python模块后,调用代码如下:
a_list= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] b=A.myfunc(3,a_list) print (b)
得到的返回值为0,而非预期的修改后的列表,请问哪里配置错误?
错误分析与修正
你的配置存在三个核心问题:
- 临时变量生命周期失效:
intypemap里的tmp是局部临时变量,函数调用结束后就会被销毁,修改的内容根本传不回Python,还存在内存访问风险。 - 未处理Python列表到C++ vector的转换:直接传入Python列表,但SWIG默认不知道如何将其映射到
std::vector<float>*,需要引入标准vector库处理类型转换。 - argout typemap未实现返回逻辑:原函数返回int类型,你需要把修改后的vector转成Python列表并追加到返回结果中,而非空实现。
修正后的SWIG接口文件
%module Point %{ #include "Point.h" %} // 引入SWIG标准vector库,实现Python列表与std::vector的双向转换 %include <std_vector.i> %template(FloatVec) std::vector<float>; // 重新定义in typemap:将Python列表转为std::vector<float>,让函数指针指向它 %typemap(in) std::vector<float>* (std::vector<float> vec) { if (!PyList_Check($input)) { PyErr_SetString(PyExc_TypeError, "参数必须是列表"); return NULL; } // 先把Python列表的元素复制到临时vector vec.resize(PyList_Size($input)); for (Py_ssize_t i = 0; i < PyList_Size($input); ++i) { PyObject* item = PyList_GetItem($input, i); if (!PyFloat_Check(item) && !PyLong_Check(item)) { PyErr_SetString(PyExc_TypeError, "列表元素必须是数字"); return NULL; } vec[i] = static_cast<float>(PyFloat_AsDouble(item)); } $1 = &vec; } // 在argout typemap里把修改后的vector转为Python列表,追加到返回值中 %typemap(argout) std::vector<float>* { PyObject* list = PyList_New($1->size()); for (size_t i = 0; i < $1->size(); ++i) { PyList_SetItem(list, i, PyFloat_FromDouble((*$1)[i])); } // 将列表作为第二个返回值,原函数的int返回值为第一个 $result = SWIG_Python_AppendOutput($result, list); } %include "Point.h"
修正后的调用方式
import Point # 创建Point类实例 A = Point.Point() a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 现在会返回两个值:原函数的int返回值、修改后的列表 ret_val, modified_list = A.myfunc(3, a_list) print(ret_val) # 输出0 print(modified_list) # 输出原列表追加[0.0, 3.0, 6.0, ..., 30.0]后的结果
可选:直接返回修改后的列表
如果想忽略原int返回值,直接返回修改后的列表,可调整argout typemap:
%typemap(argout) std::vector<float>* { PyObject* list = PyList_New($1->size()); for (size_t i = 0; i < $1->size(); ++i) { PyList_SetItem(list, i, PyFloat_FromDouble((*$1)[i])); } // 直接替换返回值为修改后的列表 Py_XDECREF($result); $result = list; }
调用代码简化为:
modified_list = A.myfunc(3, a_list) print(modified_list)
内容的提问来源于stack exchange,提问作者Pierre
相关产品推荐
相关产品推荐

