C++调用Cython回调函数出现值损坏及段错误问题求助
问题根源
你遇到的核心问题是Cython类成员函数的调用约定和C函数指针不匹配:
- Cython的
cdef void callback(self, float *vec)是类成员函数,底层需要接收一个隐式的self指针作为第一个参数 - 但你定义的C回调类型
void (*)(float*)没有预留self参数的位置,当C调用这个函数指针时,会把vec指针放到第一个参数的位置,而函数本身期望第一个参数是self,导致vec被当成无效的self指针,真正的向量数据位置错位,出现垃圾值甚至段错误。
解决方案
需要修改回调类型,显式传递self指针,让C++和Cython的调用约定对齐:
1. 修改C++代码
调整回调类型,增加一个void*参数用来传递Cython对象实例:
// 调整回调类型,增加void*参数传递Cython对象 typedef void (*callback)(void* user_data, float *vec); class CppClass{ public: callback c; void* user_data; void set_callback(callback c, void* data) { this->c = c; this->user_data = data; } // 调用回调时传入保存的user_data和vec void invoke_callback(float* vec) { if (c) { c(user_data, vec); } } }; // 包装器也要接收user_data参数 void wrapper(callback c, void* user_data) { CppClass *cls = new CppClass(); cls->set_callback(c, user_data); // 保留原有定时调用invoke_callback的逻辑 }
2. 修改Cython代码
适配新的回调类型,显式传递self指针,并在回调函数里把void*转换回CyClass实例:
cdef extern from "wrapper.h": # 调整wrapper的参数,增加void* user_data cdef void wrapper(void (*callback)(void*, float*), void* user_data) # 新的回调类型,对应C++的callback ctypedef void (*callback_t)(void*, float*) cdef class CyClass: cdef void init(self): # 传递回调函数和当前self指针 wrapper(<callback_t>&CyClass.callback_trampoline, <void*>self) # 跳板函数:处理void*到CyClass的转换,再调用实际的成员函数 cdef static void callback_trampoline(void* user_data, float *vec): cdef CyClass self = <CyClass>user_data self.callback(vec) # 实际的回调逻辑,保持原来的签名 cdef void callback(self, float *vec): print(vec[0])
原理说明
- 新增的
callback_trampoline是静态函数,符合C函数指针的调用约定(不需要隐式self) - 调用wrapper时把CyClass的
self指针作为user_data传给C++,C++保存这个指针 - 当C++触发回调时,会把
user_data和vec一起传给callback_trampoline,后者把user_data转换回CyClass实例,再调用成员函数callback,此时参数传递完全正确。
内容的提问来源于stack exchange,提问作者George Huang
相关产品推荐
相关产品推荐

