惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
Intezer
S
Schneier on Security
AI
AI
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
Forbes - Security
Forbes - Security
L
Lohrmann on Cybersecurity
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Help Net Security
Help Net Security
A
Arctic Wolf
T
Troy Hunt's Blog
Scott Helme
Scott Helme
T
The Exploit Database - CXSecurity.com
S
Security @ Cisco Blogs
T
Threatpost
The Cloudflare Blog
月光博客
月光博客
Cyberwarzone
Cyberwarzone
S
Security Affairs
M
MIT News - Artificial intelligence
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
H
Hacker News: Front Page
P
Privacy International News Feed
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary
宝玉的分享
宝玉的分享
V
V2EX
F
Fortinet All Blogs
罗磊的独立博客
B
Blog RSS Feed
腾讯CDC
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - Franky
Security Latest
Security Latest

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
pybind11函数指针入门到放弃
SkyOnSky · 2026-05-17 · via 博客园_首页

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明

  无

前言


  在我们想把底层C++/C的接口暴露给python的时候,我们一般采用的是pybind11框架,可以很方便的让我们暴露接口。对于普通的接口参数类型来说,pybind11也提供了适合的转换类型(例如:std::vector/uint64_t 等等)。但是年前的一个项目中,遇到了一类特别的参数,函数指针,让我一度很头疼,导致我遇到了如下几个问题:

  • 问题1:python的函数怎么转换为pybind11中的std::function(底层原理)
  • 问题2:pybind11中的std::function 怎么转换为c++层的裸函数
  • 问题3:pybind11中的std::function对象复制的注意事项 (问题3出现的原因和我解决问题2有关系,如果不注意,会导致出现python解释器报错)

  下面来看看我遇到上面的几个问题的实际例子。

  事情是这样的,我有一个c++接口,接收一个类似void(*CB)(Data&)的函数指针,当我尝试将此接口暴露给python的时候,AI一步到位给我写好了转换代码如下:

#include <cstdio>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
namespace py = pybind11;

typedef void(*CB)(int&);

class Test{
    public:
    void test(CB cb)
    {
        printf("printf");
    }
};

PYBIND11_MODULE(testpy, m) {
    m.doc() = "pybind11 wrapper for testpy class";
    py::class_<Test>(m, "Test")
    .def(py::init<>(), "Constructor for the Test class")
    .def("test", [](Test& self, std::function<void(int&)> callback) {
        //... ...
        auto cb = [callback](int&d){
            callback(d);
        };
        // 下面这行报错,错误信息类似: error: cannot convert ‘pybind11_init_testpy(pybind11::module_&)::<lambda(Test&, std::function<void(int&)>)>::<lambda(int&)>’ to ‘CB’ {aka ‘void (*)(int&)’}
        return self.test(cb);
    }

}            
import testpy
def test_cb(i):
    print(f'test_cb i = {i}')
t = testpy.Test()
t.test()

  首先,这里有个结论,AI给出的代码是错误的(带捕获参数的lambda不能直接转换为裸指针,可看注释部分),但是形式上面给出了转换代码的大致框架,这里精简后,整个问题的核心就是:pybind11的std::function怎么转换为函数指针。

  下面我们看看怎么解决上面3个问题,并解决最终问题。

问题1:python的函数怎么转换为pybind11中的std::function(底层原理)


  要解决这个问题,我们得了解python的函数传递过来,这里的callback到底是什么东西?由于pybind11是大量的宏来生成代码,为了快速得到我们要的内容,我们使用gdb来对堆栈进行分析。

#0  test_cb (i=@0x7fffffffd714: 9999) at testpy.cpp:19
#1  0x00007ffff73bfded in Test::test (this=0xc49820, cb=0x7ffff73ab120 <test_cb(int&)>) at testpy.cpp:13
#2  0x00007ffff73ab3f7 in operator()(Test &, std::function<void(int&)>) const (__closure=0xc49678, self=..., callback=...) at testpy.cpp:33
#3  0x00007ffff73ac08c in pybind11::detail::argument_loader<Test&, std::function<void(int&)> >::call_impl<void, pybind11_init_testpy(pybind11::module_&)::<lambda(Test&, std::function<void(int&)>)>&, 0, 1, pybind11::detail::void_type>(struct {...} &, std::index_sequence, pybind11::detail::void_type &&) (this=0x7fffffffd8a0, f=...) at /usr/include/pybind11/cast.h:1480
#4  0x00007ffff73abd00 in pybind11::detail::argument_loader<Test&, std::function<void(int&)> >::call<void, pybind11::detail::void_type, pybind11_init_testpy(pybind11::module_&)::<lambda(Test&, std::function<void(int&)>)>&>(struct {...} &) (this=0x7fffffffd8a0, f=...) at /usr/include/pybind11/cast.h:1454
#5  0x00007ffff73ab9b7 in operator() (__closure=0x0, call=...) at /usr/include/pybind11/pybind11.h:254
#6  0x00007ffff73aba6c in _FUN () at /usr/include/pybind11/pybind11.h:224
#7  0x00007ffff73bd52c in pybind11::cpp_function::dispatcher (self=0x7ffff75fedc0, args_in=0x7ffff7421f80, kwargs_in=0x0) at /usr/include/pybind11/pybind11.h:946
#8  0x0000000000581ecf in cfunction_call (func=0x7ffff740ba10, args=<optimized out>, kwargs=<optimized out>) at ../Objects/methodobject.c:537
#9  0x0000000000549205 in _PyObject_MakeTpCall (tstate=0xba5748 <_PyRuntime+459656>, callable=0x7ffff740ba10, args=<optimized out>, nargs=2, keywords=0x0) at ../Objects/call.c:240
#10 0x0000000000549c3d in _PyObject_VectorcallTstate (kwnames=<optimized out>, nargsf=<optimized out>, args=<optimized out>, callable=<optimized out>, tstate=<optimized out>) at ../Include/internal/pycore_call.h:90
#11 0x00000000005d7109 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0xba5748 <_PyRuntime+459656>, frame=<optimized out>, frame@entry=0x7ffff7fb2020, throwflag=throwflag@entry=0) at Python/bytecodes.c:2706
#12 0x00000000005d564b in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7fb2020, tstate=0xba5748 <_PyRuntime+459656>) at ../Include/internal/pycore_ceval.h:89
#13 _PyEval_Vector (kwnames=0x0, argcount=0, args=0x0, locals=0x7ffff75f9a80, func=0x7ffff75da160, tstate=0xba5748 <_PyRuntime+459656>) at ../Python/ceval.c:1683
#14 PyEval_EvalCode (co=co@entry=0x7ffff75604b0, globals=globals@entry=0x7ffff75f9a80, locals=locals@entry=0x7ffff75f9a80) at ../Python/ceval.c:578
#15 0x00000000006087b2 in run_eval_code_obj (locals=0x7ffff75f9a80, globals=0x7ffff75f9a80, co=0x7ffff75604b0, tstate=0xba5748 <_PyRuntime+459656>) at ../Python/pythonrun.c:1722
#16 run_mod (mod=<optimized out>, filename=<optimized out>, globals=0x7ffff75f9a80, locals=0x7ffff75f9a80, flags=<optimized out>, arena=<optimized out>) at ../Python/pythonrun.c:1743
#17 0x00000000006b4853 in pyrun_file (fp=fp@entry=0xbf6480, filename=filename@entry=0x7ffff7409ca0, start=start@entry=257, globals=globals@entry=0x7ffff75f9a80, locals=locals@entry=0x7ffff75f9a80, closeit=closeit@entry=1, flags=0x7fffffffe0a8) at ../Python/pythonrun.c:1643
#18 0x00000000006b45ba in _PyRun_SimpleFileObject (fp=fp@entry=0xbf6480, filename=filename@entry=0x7ffff7409ca0, closeit=closeit@entry=1, flags=flags@entry=0x7fffffffe0a8) at ../Python/pythonrun.c:433
#19 0x00000000006b43ef in _PyRun_AnyFileObject (fp=0xbf6480, filename=filename@entry=0x7ffff7409ca0, closeit=closeit@entry=1, flags=flags@entry=0x7fffffffe0a8) at ../Python/pythonrun.c:78
#20 0x00000000006bc455 in pymain_run_file_obj (skip_source_first_line=0, filename=0x7ffff7409ca0, program_name=0x7ffff75f9bf0) at ../Modules/main.c:360
#21 pymain_run_file (config=0xb48328 <_PyRuntime+77672>) at ../Modules/main.c:379
#22 pymain_run_python (exitcode=0x7fffffffe09c) at ../Modules/main.c:629
#23 Py_RunMain () at ../Modules/main.c:709
#24 0x00000000006bbf3d in Py_BytesMain (argc=<optimized out>, argv=<optimized out>) at ../Modules/main.c:763
#25 0x00007ffff7c2a1ca in __libc_start_call_main (main=main@entry=0x518ac0 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffe2e8) at ../sysdeps/nptl/libc_start_call_main.h:58
#26 0x00007ffff7c2a28b in __libc_start_main_impl (main=0x518ac0 <main>, argc=2, argv=0x7fffffffe2e8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe2d8) at ../csu/libc-start.c:360
#27 0x00000000006574f5 in _start ()

  从上面看,核心就是从python解释器,到了pybind11::cpp_function::dispatcher,然后到了我们的test函数。如果我们对pybind11不熟悉的话,我们还是需要深入去看pybind11才能回答我们上面的问题,但是我这里想到了另外一个办法。

  我们都知道,pybind11底层是由cpython实现的,因此我们通过cpython来实现上面的同样的功能是什么样子的呢?直接让AI生成示例如下:


 #define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <cstdio>
#include <functional>

// --- C++ 逻辑模拟部分 ---
typedef void(*CB)(int&);

class Test{
    public:
    void test(CB cb)
    {
        int i = 9999;
        cb(i);
    }
};

void test_cb(int& i)
{
    printf("test_cb from cxx i = %d\n", i);
}

// --- CPython 包装部分 ---

// 定义 Python 中的 Test 对象结构
typedef struct {
    PyObject_HEAD
    Test* cpp_obj; // 指向实际的 C++ 对象
} PyTestObject;

void pybind11_like_func(Test& self, std::function<void(int&)> callback)
{
    //... ...
    auto cb = [callback](int&d){
        callback(d);
    };
    int i = 8888;
    cb(i);
    // return self.test(cb);
    return self.test(test_cb);
}

// Test.test(callback) 的实现
static PyObject* PyTest_test(PyTestObject* self, PyObject* args) {
    PyObject* pycallback = NULL;

    // 1. 解析参数,期望得到一个可调用对象
    if (!PyArg_ParseTuple(args, "O", &pycallback)) {
        return NULL;
    }

    if (!PyCallable_Check(pycallback)) {
        PyErr_SetString(PyExc_TypeError, "Parameter must be callable");
        return NULL;
    }

    // 2. 核心:模拟 std::function<void(int&)> 的构造
    // 我们在这里捕获 py_callback 指针。注意:实际生产中需要处理引用计数
    std::function<void(int&)> cpp_callback = [pycallback](int& d) {
        // A. 必须获取 GIL,因为回调可能由 C++ 触发
        PyGILState_STATE gstate = PyGILState_Ensure();

        // B. 参数转换:C++ int& -> Python Long
        PyObject* arg = PyLong_FromLong((long)d);
        PyObject* arg_tuple = PyTuple_Pack(1, arg);

        // C. 调用 Python 函数
        PyObject* result = PyObject_CallObject(pycallback, arg_tuple);

        // D. 错误处理与清理
        if (!result) {
            PyErr_Print(); 
        }

        Py_XDECREF(result);
        Py_DECREF(arg_tuple);
        Py_DECREF(arg);

        // E. 释放 GIL
        PyGILState_Release(gstate);
    };

    pybind11_like_func(*self->cpp_obj, cpp_callback);
    Py_RETURN_NONE;
}

// --- 类型与模块定义 ---

static PyMethodDef PyTest_methods[] = {
    {"test", (PyCFunction)PyTest_test, METH_VARARGS, "Execute test with callback"},
    {NULL, NULL, 0, NULL}
};


static PyTypeObject PyTestType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "testcpy.Test",
    .tp_basicsize = sizeof(PyTestObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_methods = PyTest_methods,
    .tp_new = PyType_GenericNew,
};


static struct PyModuleDef testcpymodule = {
    PyModuleDef_HEAD_INIT,
    "testcpy",
    "CPython version of testcpy",
    -1,
    NULL
};


PyMODINIT_FUNC PyInit_testcpy(void) {
    PyObject* m;
    if (PyType_Ready(&PyTestType) < 0) return NULL;
    m = PyModule_Create(&testcpymodule);
    if (m == NULL) return NULL;

    Py_INCREF(&PyTestType);
    PyModule_AddObject(m, "Test", (PyObject*)&PyTestType);
    return m;
}

  其实我们已经看到了,我们用cpython来实现的话,调用的std::function一定是一个带状态的callable obj。至此,我们已经解决了问题1。

问题2:pybind11中的std::function 怎么转换为c++层的裸函数


  实际的方法就是在pybind11代码层,添加一个全局静态变量进行转换,参考如下代码(重点查看PyCBWrapper相关的内容):

#include <cstdio>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>

#include <functional>
namespace py = pybind11;
typedef void(*CB)(int&);

class Test{
    public:
    void test(CB cb)
    {
        int i = 9999;
        cb(i);
    }
};

struct PyCBWrapper {

    static std::function<void(int&)> py_cb;

    static void trampoline(int& i) {
        
        if (nullptr != py_cb)
            PyCBWrapper::py_cb(i);
        
    }
};


std::function<void(int&)> PyCBWrapper::py_cb = nullptr;


void test_cb(int& i)
{
    printf("test_cb from cxx i = %d\n", i);
}

PYBIND11_MODULE(testpy, m) {
    m.doc() = "pybind11 wrapper for testpy class";
    py::class_<Test>(m, "Test")
    .def(py::init<>(), "Constructor for the Test class")
    .def("test", [](Test& self, std::function<void(int&)> callback) {
        //... ...
        auto cb = [callback](int&d){
            callback(d);
        };
        int i = 8888;
        cb(i);
        PyCBWrapper::py_cb = callback;
        // return self.test(cb);
        return self.test(PyCBWrapper::trampoline);
    });

}    

问题3:pybind11中的std::function对象复制的注意事项


  问题2的这段代码会直接运行报错,堆栈如下

#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=<optimized out>) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7c4527e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c288ff in __GI_abort () at ./stdlib/abort.c:79
#5  0x00000000004b1252 in ?? ()
#6  0x00000000004b2908 in _Py_FatalErrorFunc ()
#7  0x00000000004b2cd2 in ?? ()
#8  0x000000000060861e in ?? ()
#9  0x00000000006a6e93 in PyEval_AcquireThread ()
#10 0x00007ffff73b9d20 in pybind11::gil_scoped_acquire::gil_scoped_acquire (this=0x7fffffffd760) at /usr/include/pybind11/gil.h:82
#11 0x00007ffff73dd1b1 in pybind11::detail::type_caster<std::function<void (int&)>, void>::load(pybind11::handle, bool)::func_handle::~func_handle() (this=0xc49870, __in_chrg=<optimized out>)
    at /usr/include/pybind11/functional.h:97
#12 0x00007ffff73dd324 in pybind11::detail::type_caster<std::function<void (int&)>, void>::load(pybind11::handle, bool)::func_wrapper::~func_wrapper() (this=0xc49870, __in_chrg=<optimized out>)
    at /usr/include/pybind11/functional.h:103
#13 0x00007ffff73e0ff2 in std::_Function_base::_Base_manager<pybind11::detail::type_caster<std::function<void(int&)>, void>::load(pybind11::handle, bool)::func_wrapper>::_M_destroy (__victim=...)
    at /usr/include/c++/13/bits/std_function.h:175
--Type <RET> for more, q to quit, c to continue without paging--
#14 0x00007ffff73e0ce4 in std::_Function_base::_Base_manager<pybind11::detail::type_caster<std::function<void(int&)>, void>::load(pybind11::handle, bool)::func_wrapper>::_M_manager (__dest=..., __source=..., 
    __op=std::__destroy_functor) at /usr/include/c++/13/bits/std_function.h:203
#15 0x00007ffff73e02aa in std::_Function_handler<void(int&), pybind11::detail::type_caster<std::function<void(int&)>, void>::load(pybind11::handle, bool)::func_wrapper>::_M_manager (__dest=..., __source=..., 
    __op=std::__destroy_functor) at /usr/include/c++/13/bits/std_function.h:282
#16 0x00007ffff73b62c1 in std::_Function_base::~_Function_base (this=0x7ffff73ff440 <PyCBWrapper::py_cb>, __in_chrg=<optimized out>) at /usr/include/c++/13/bits/std_function.h:244
#17 0x00007ffff73bff78 in std::function<void(int&)>::~function (this=0x7ffff73ff440 <PyCBWrapper::py_cb>, __in_chrg=<optimized out>) at /usr/include/c++/13/bits/std_function.h:334
#18 0x00007ffff7c47a76 in __run_exit_handlers (status=0, listp=<optimized out>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:108
#19 0x00007ffff7c47bbe in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:138
#20 0x00007ffff7c2a1d1 in __libc_start_call_main (main=main@entry=0x518c60, argc=argc@entry=2, argv=argv@entry=0x7fffffffda28) at ../sysdeps/nptl/libc_start_call_main.h:74
#21 0x00007ffff7c2a28b in __libc_start_main_impl (main=0x518c60, argc=2, argv=0x7fffffffda28, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffda18)
    at ../csu/libc-start.c:360
#22 0x0000000000657b05 in _start ()

  从堆栈分析可知,问题出在程序退出的时候,PyCBWrapper::py_cb(全局静态变量)析构的时候,这个时候其实python解释器已经退出了,再使用python解释器相关的资源,就会报错。这个其实就是问题3。由于从上面的例子可以知道,这个时候的PyCBWrapper::py_cb是一个捕获了python函数的PyObject的std::function,那么解决方案也很简单,那就是在上面的pybind11代码层中添加如下核心代码:

PYBIND11_MODULE(testpy, m) {
    m.doc() = "pybind11 wrapper for testpy class";
    py::class_<Test>(m, "Test")
    .def(py::init<>(), "Constructor for the Test class")
    .def("test", [](Test& self, std::function<void(int&)> callback) {
        //... ...
        auto cb = [callback](int&d){
            callback(d);
        };
        int i = 8888;
        cb(i);
        PyCBWrapper::py_cb = callback;
        // return self.test(cb);
        self.test(PyCBWrapper::trampoline);
        // 核心代码
        PyCBWrapper::py_cb = nullptr;
        return;
    });

}    

  这里的核心就是将PyCBWrapper::py_cb = nullptr;置为空,保证pyobject变量在python解释器还正常的工作时候进行析构。问题3解决,完结散花。

后记


  其实在了解了cpython转换python函数为std::function的细节,解决这些奇怪的问题还是很简单的。

  在AI的帮助下,解决这些问题只需要有方向即可,验证时间及方法已经非常的快了。

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。