关于函数指针的一些问题小结
最近接到一个需求,使用 sdk 提供的消息回调,一般我们是继承 sdk 的消息类,然后 sdk 的消息回调(虚函数)会在有消息的时候调用回调指针,从而触发回调
不过因为 sdk 那边又对该消息类二次封装了并提供了一些接口,所以在研究二次封装的方法时,遇到了一些有意思的问题,故记录下
1 2 3 | typedef void (__stdcall* fSDKLogCallback)( void * ctx, int32_t severity, const char * message, uint32_t message_size); |
1 | std::pair< void *, fSDKLogCallback> sdk_log_cb_; |
在编译时,如果删除 __stdcall 调用约定后,
1 2 3 | typedef void * fSDKLogCallback( void * ctx, int32_t severity, const char * message, uint32_t message_size); |
程序会在 std::pair 处报错
1 | error C2207: 'std::pair<_Ty1,_Ty2>::second' : a member of a class template cannot acquire a function type |
这是因为 std::pair
的模板参数要求是可复制构造的类型,而函数类型是不可复制构造的。因此,如果你直接使用没有 __stdcall
的函数指针类型作为 std::pair
的模板参数之一,会导致编译错误。
通过将函数指针类型添加 __stdcall
关键字,你可以将其定义为可复制构造的类型,并在 std::pair
的模板参数中使用它,从而避免编译错误。
补充:
__stdcall
是一种调用约定,它指定了函数调用时参数的传递方式、栈的清理方式以及函数调用的规则。在某些情况下,特别是在 Windows 平台的一些 API 中,函数指针的类型定义可能要求使用 __stdcall
调用约定。
当你定义一个函数指针类型 fSDKLogCallback
时,如果你的实际使用场景要求使用 __stdcall
调用约定,那么你必须在函数指针类型的定义中显式添加 __stdcall
关键字。
后续:
要使用二次封装的接口,首先要将我们自己定义的回调函数地址传给接口
// 声明 void SetSDKLogCallback(void* ctx, fSDKLogCallback cb); // 定义 void XXX::SetSDKLogCallback(void* ctx, fSDKLogCallback cb) { sdk_log_cb_.first = ctx; sdk_log_cb_.second = cb; }
在原始 sdk 的回调里直接调用该地址
void on_sdk_log(int severity, const char* message, size_t message_size) override { if (sdk_log_cb_.second) { sdk_log_cb_.second(sdk_log_cb_.first, severity, message, (uint32_t)message_size); } }
最终在我们项目中使用
1 2 3 4 5 6 7 8 | // interface_ptr 是二次封装类的接口指针 // onSDKLog 是我们自己定义的回调 interface_ptr->SetSDKLogCallback( this , onSDKLog); void ::onSDKLog( void * ctx, int severity, const char * message, size_t message_size) { VLOG(1) << message; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】