如何在C++类中封装C回调函数以适配指定接口
问题解决方案
核心问题分析
你需要将std::function<void()>适配为C库要求的void(*)(handle_t)回调类型,同时解决以下两个关键问题:
- 类型不兼容:
std::function<void()>无法直接转换为C风格函数指针 - 生命周期与多实例:局部lambda或成员函数无法安全传递给C回调
方案1:单实例场景(简单直接)
如果你的应用仅需一个MyClass实例,可使用静态成员存储回调,静态函数作为C回调的适配层:
#include <functional> // 外部C库定义 typedef void* handle_t; extern "C" void register_callback(void(*callback)(handle_t handle)); class MyClass { private: // 静态成员存储用户传入的回调 static std::function<void()> s_user_callback; // 静态适配函数,完全符合C回调签名 static void callback_adapter(handle_t) { // 忽略无用的handle_t参数,直接调用用户回调 if (s_user_callback) { s_user_callback(); } } public: MyClass(std::function<void()> callback) { s_user_callback = std::move(callback); // 注册静态适配函数到C库 register_callback(callback_adapter); } }; // 初始化静态成员 std::function<void()> MyClass::s_user_callback;
方案2:多实例场景(利用handle_t关联)
如果需要支持多个MyClass实例,可通过全局映射表将C回调传入的handle_t与实例绑定(即使你认为handle无用,也可用来关联实例):
#include <functional> #include <unordered_map> #include <mutex> // 外部C库定义 typedef void* handle_t; extern "C" void register_callback(void(*callback)(handle_t handle)); class MyClass { private: std::function<void()> m_user_callback; handle_t m_associated_handle; // 与当前实例绑定的handle // 全局映射表:handle -> MyClass实例(线程安全) static std::unordered_map<handle_t, MyClass*> s_instance_map; static std::mutex s_map_mutex; // 静态适配函数 static void callback_adapter(handle_t handle) { std::lock_guard<std::mutex> lock(s_map_mutex); auto it = s_instance_map.find(handle); if (it != s_instance_map.end()) { it->second->m_user_callback(); } } public: // 构造时传入用户回调和关联的handle MyClass(std::function<void()> callback, handle_t associated_handle) : m_user_callback(std::move(callback)), m_associated_handle(associated_handle) { std::lock_guard<std::mutex> lock(s_map_mutex); s_instance_map[m_associated_handle] = this; register_callback(callback_adapter); } // 析构时从映射表移除实例,避免悬空指针 ~MyClass() { std::lock_guard<std::mutex> lock(s_map_mutex); s_instance_map.erase(m_associated_handle); } }; // 初始化静态成员 std::unordered_map<handle_t, MyClass*> MyClass::s_instance_map; std::mutex MyClass::s_map_mutex;
原代码失败原因
- 类型不匹配:直接传递
std::function<void()>给register_callback,两者类型完全不兼容,编译会报错 - 生命周期问题:局部
callback_wrapper在构造函数结束后销毁,C库后续触发回调时会访问已销毁对象,导致未定义行为 - 捕获引用的lambda无法转成函数指针:只有无捕获的lambda才能隐式转换为普通函数指针,捕获变量的lambda不行
内容的提问来源于stack exchange,提问作者vict
相关产品推荐
相关产品推荐

