You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

原理说明

  1. create_symbol_list返回std::unique_ptr<std::list<Symbol*>>,pybind11会自动将其转换为Python对象,由Python的垃圾回收机制管理生命周期。
  2. py::keep_alive<1,2>确保ParamMap对象存活时,传入的列表指针对应的C++对象不会被销毁。
  3. 手动添加Symbol到C++列表,避免了自动转换带来的临时对象问题。

内容的提问来源于stack exchange,提问作者theseventhsu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:27:33