pybind11 STL自动转换器致std::list指针失效问题求助
问题描述
使用pybind11将操作std::list<Symbol*>*类型的C库转换为Python接口时,将Python列表自动转换为该C列表并初始化持有该列表的ParamMap对象后,列表指针出现异常,GDB调试发现对象的next指针无效,遍历列表触发段错误。
已排查:C侧ParamMap容器和Symbol对象的析构函数均未被调用,排除对象被释放的可能。尝试过py::return_value_policy::reference和py::keep_alive策略,问题仍未解决。因无法修改C侧的列表类型,需解决Python侧的适配问题。
最小复现代码
Symbol.hpp
#include <string> class Symbol { private: std::string val1; int val2; public: Symbol(std::string con1, int con2) : val1(con1), val2(con2) {} };
ParamMap.hpp
#include <list> #include "Symbol.hpp" class ParamMap { private: std::list<Symbol*>* lst; int otherData; public: ParamMap(std::list<Symbol*>* symbolList, int dat) : lst(symbolList), otherData(dat) {} std::list<Symbol*>* getSymbols() { return lst; } int getOtherData() { return otherData; } };
Query.cpp
#include <iostream> #include "ParamMap.hpp" #include <pybind11/pybind11.h> #include <pybind11/stl.h> namespace py = pybind11; void getSymbolListSize(ParamMap* map) { std::cout << "Entering query method" << std::endl; auto sz = map->getSymbols()->size(); // SEGFAULT OCCURS WHEN GETTING SIZE std::cout << "Got size successfully. Size = " << sz << std::endl; } PYBIND11_MODULE(list_test, handle) { handle.def("getSymbolListSize", &getSymbolListSize); py::class_<ParamMap>(handle, "ParamMap") .def(py::init<std::list<Symbol*>*, int>(), py::keep_alive<1, 2>()) .def("getOtherData", &ParamMap::getOtherData) .def("getSymbols", &ParamMap::getSymbols); py::class_<Symbol>(handle, "Symbol") .def(py::init<std::string, int>()); }
test.py
import list_test as p # Creating a list of some random symbols symbol_list = [] symbol1 = p.Symbol("Hello", 1) symbol_list.append(symbol1) symbol2 = p.Symbol("World", 2) symbol_list.append(symbol2) # Creating a parammap and passing it the symbol list pm = p.ParamMap(symbol_list, 71) print("Symbol list and ParamMap init'd successfully") # Here, calling Query.cpp's only method sz = p.getSymbolListSize(pm) print(sz)
解决方案
问题根源是:pybind11自动将Python列表转换为临时的std::list<Symbol*>对象,这个临时对象在ParamMap构造函数调用完成后就会被销毁,导致ParamMap持有的指针悬空,后续访问时触发段错误。py::keep_alive<1,2>无效是因为它只能关联Python可见的对象,而临时的C++列表并没有被Python持有。
解决思路是让Python持有C++列表的所有权,确保其生命周期覆盖ParamMap的使用周期,具体实现如下:
修改Query.cpp的绑定代码
添加手动创建std::list<Symbol*>的函数,返回std::unique_ptr让Python管理其生命周期,同时调整ParamMap的绑定逻辑:
#include <iostream> #include "ParamMap.hpp" #include <memory> #include <pybind11/pybind11.h> #include <pybind11/stl.h> namespace py = pybind11; void getSymbolListSize(ParamMap* map) { std::cout << "Entering query method" << std::endl; auto sz = map->getSymbols()->size(); std::cout << "Got size successfully. Size = " << sz << std::endl; } // 手动创建C++列表并返回,让Python持有所有权 std::unique_ptr<std::list<Symbol*>> create_symbol_list() { return std::make_unique<std::list<Symbol*>>(); } // 向C++列表中添加Symbol对象 void add_symbol_to_list(std::list<Symbol*>* lst, Symbol* sym) { lst->push_back(sym); } PYBIND11_MODULE(list_test, handle) { handle.def("getSymbolListSize", &getSymbolListSize); handle.def("create_symbol_list", &create_symbol_list); handle.def("add_symbol_to_list", &add_symbol_to_list); py::class_<ParamMap>(handle, "ParamMap") .def(py::init<std::list<Symbol*>*, int>(), py::keep_alive<1, 2>()) // 确保ParamMap存活时列表不被销毁 .def("getOtherData", &ParamMap::getOtherData) .def("getSymbols", &ParamMap::getSymbols, py::return_value_policy::reference); // 返回列表指针的引用 py::class_<Symbol>(handle, "Symbol") .def(py::init<std::string, int>()); }
修改test.py代码
通过手动创建的C++列表来构建ParamMap:
import list_test as p # 创建C++侧的std::list<Symbol*>对象 cpp_symbol_list = p.create_symbol_list() # 添加Symbol到C++列表 symbol1 = p.Symbol("Hello", 1) p.add_symbol_to_list(cpp_symbol_list, symbol1) symbol2 = p.Symbol("World", 2) p.add_symbol_to_list(cpp_symbol_list, symbol2) # 创建ParamMap,此时cpp_symbol_list由Python持有,生命周期和pm绑定 pm = p.ParamMap(cpp_symbol_list, 71) print("Symbol list and ParamMap init'd successfully") # 调用方法不再触发段错误 p.getSymbolListSize(pm)
原理说明
create_symbol_list返回std::unique_ptr<std::list<Symbol*>>,pybind11会自动将其转换为Python对象,由Python的垃圾回收机制管理生命周期。py::keep_alive<1,2>确保ParamMap对象存活时,传入的列表指针对应的C++对象不会被销毁。- 手动添加Symbol到C++列表,避免了自动转换带来的临时对象问题。
内容的提问来源于stack exchange,提问作者theseventhsu
相关产品推荐
相关产品推荐

