如何通过SWIG将Python列表转为std::vector<double>*传入C++构造函数
问题根源
SWIG默认的std::vector<double>* INPUT类型映射会创建临时vector对象:当调用函数/构造函数时,SWIG将Python列表转换为临时vector,传递指针给C++代码;调用结束后,SWIG会销毁这个临时vector。
对于成员方法(如SampleMethod),临时vector仅在方法执行期间有效,执行完毕销毁不会有问题;但构造函数会将这个临时vector的指针保存到m_v成员变量中,后续访问时临时vector已被销毁,指针变为野指针,从而导致异常值、乱码甚至段错误。
解决方案
由于无法修改C代码,我们需要在SWIG接口中调整类型映射逻辑,确保构造函数接收的vector不会被SWIG自动销毁,或让vector的生命周期与C对象绑定。以下提供两种可行方案:
方案1:使用OWN类型映射接管所有权
SWIG的OWN类型映射会告知SWIG:传递的指针指向的对象将被C++代码接管所有权,SWIG不会自动销毁它。修改example.i如下:
%module example %{ #include "example.hpp" %} %include "typemaps.i" %include "std_vector.i" %template(doublevector) std::vector<double>; // 针对构造函数的vector指针参数,应用OWN类型映射 %apply std::vector<double> *OWN {std::vector<double>* v}; %include "example.hpp"
注意事项
- 该方案会导致内存泄漏:C类
SampleClass的代码中没有销毁m_v的逻辑,因此vector的内存不会被释放。如果你的实际场景中C类会负责销毁m_v,则此方案完全适用。
方案2:自定义构造函数包装(无内存泄漏)
通过%extend在SWIG中自定义构造函数,手动创建vector并将其生命周期与Python代理对象绑定,当Python代理被垃圾回收时自动销毁vector。修改example.i如下:
%module example %{ #include "example.hpp" %} %include "std_vector.i" %template(doublevector) std::vector<double>; // 手动包装SampleClass,自定义构造函数处理Python列表 class SampleClass { public: std::vector<double>* m_v; // 自定义构造函数:接收Python列表,创建持久化vector %extend { SampleClass(PyObject* py_list) { // 校验输入类型 if (!PyList_Check(py_list)) { PyErr_SetString(PyExc_TypeError, "必须传入列表类型参数"); return NULL; } // 创建新的vector并填充数据 std::vector<double>* vec = new std::vector<double>(); const int list_size = PyList_Size(py_list); for (int i = 0; i < list_size; ++i) { PyObject* item = PyList_GetItem(py_list, i); if (!PyFloat_Check(item)) { delete vec; // 提前清理避免内存泄漏 PyErr_SetString(PyExc_TypeError, "列表元素必须是浮点数"); return NULL; } vec->push_back(PyFloat_AsDouble(item)); } // 创建C++对象 SampleClass* instance = new SampleClass(vec); // 将vector与Python代理对象绑定,代理销毁时自动删除vector $module_attachptr(instance, vec, SWIGTYPE_p_std__vectorT_double_t); return instance; } } // 保留原有构造函数和方法声明 SampleClass(); void SampleMethod(std::vector<double>* arg); void SampleMethod2(std::vector<double>* arg); void print_member(); }; // 为成员方法保留原有的INPUT类型映射 %apply std::vector<double> *INPUT {std::vector<double>* arg};
优势
- 无内存泄漏:当Python中的
SampleClass实例被垃圾回收时,SWIG会自动销毁绑定的vector。 - 输入校验:可以在构造函数中添加类型检查,提前抛出Python类型错误,避免C++端崩溃。
测试验证
修改后重新编译运行,Python端调用构造函数时:
import example as ex b = ex.SampleClass([1.2, 3.1, 4.1]) print(b.m_v.size()) # 输出3,正常 b.print_member() # 输出1.2 3.1 4.1,无段错误
内容的提问来源于stack exchange,提问作者LateBird
相关产品推荐
相关产品推荐

