MSVC下使用callback_return_type_t指定Lambda返回类型失败问题
问题原因分析
MSVC在处理Lambda的尾随返回类型时,对依赖外部模板类型萃取的表达式的解析逻辑与GCC/Clang存在差异。尽管PLH::callback_return_type_t<decltype(pNtQueueApcthread)>的推导结果确实是unsigned long,但MSVC在Lambda的上下文环境中无法正确完成该类型的实时解析——这是因为Lambda本身是延迟实例化的匿名类型,MSVC对其尾随返回类型中的模板嵌套解析存在延迟绑定的兼容性问题。
另外,带__stdcall调用约定的函数指针类型在MSVC中的内部表示与GCC/Clang不同,这可能进一步干扰了模板类型萃取在Lambda返回类型位置的解析流程。
跨编译器兼容解决方案
方案1:提前typedef返回类型
将返回类型提前萃取并定义为独立别名,避免在Lambda的尾随返回类型中直接嵌套模板表达式:
typedef void(*PKNORMAL_ROUTINE)(void* NormalContext, void* SystemArgument1, void* SystemArgument2); typedef unsigned long(__stdcall* tNtQueueApcThread)(void* ThreadHandle, PKNORMAL_ROUTINE ApcRoutine, void* NormalContext, void* SystemArgument1, void* SystemArgument2); uint64_t hkNtQueueapcThread = NULL; tNtQueueApcThread pNtQueueApcthread = (tNtQueueApcThread)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueueApcThread"); // 提前萃取并定义返回类型别名 using NtQueueApcRetType = PLH::callback_return_type_t<decltype(pNtQueueApcthread)>; typedef PLH::callback_type_t<decltype(pNtQueueApcthread)> h_NtQueueapcThread_t; PLH::callback_type_t<decltype(pNtQueueApcthread)> hkNtQueueapcThread = PLH::make_callback(pNtQueueApcthread, [](auto... _args) -> NtQueueApcRetType { // 使用提前定义的别名 std::cout << "hkNtQueueApcThread!" << std::endl; auto ret = PLH::FnCast(hkNtQueueapcThread, pNtQueueApcthread)(_args...); return ret; });
方案2:自动推导+编译期类型校验
如果希望保留自动推导的便捷性,同时强制返回类型匹配,可以结合static_assert做编译期校验:
typedef void(*PKNORMAL_ROUTINE)(void* NormalContext, void* SystemArgument1, void* SystemArgument2); typedef unsigned long(__stdcall* tNtQueueApcThread)(void* ThreadHandle, PKNORMAL_ROUTINE ApcRoutine, void* NormalContext, void* SystemArgument1, void* SystemArgument2); uint64_t hkNtQueueapcThread = NULL; tNtQueueApcThread pNtQueueApcthread = (tNtQueueApcThread)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueueApcThread"); typedef PLH::callback_type_t<decltype(pNtQueueApcthread)> h_NtQueueapcThread_t; using ExpectedRetType = PLH::callback_return_type_t<decltype(pNtQueueApcthread)>; PLH::callback_type_t<decltype(pNtQueueApcthread)> hkNtQueueapcThread = PLH::make_callback(pNtQueueApcthread, [](auto... _args) { std::cout << "hkNtQueueApcThread!" << std::endl; auto ret = PLH::FnCast(hkNtQueueapcThread, pNtQueueApcthread)(_args...); // 编译期检查返回类型是否匹配 static_assert(std::is_same_v<decltype(ret), ExpectedRetType>, "Return type mismatch!"); return ret; });
方案3:封装宏简化类型萃取(可选)
如果项目中频繁需要这类操作,可以封装宏来简化重复代码:
#define GET_CALLBACK_RET_TYPE(FUNC_PTR) PLH::callback_return_type_t<decltype(FUNC_PTR)> // 使用示例 PLH::callback_type_t<decltype(pNtQueueApcthread)> hkNtQueueapcThread = PLH::make_callback(pNtQueueApcthread, [](auto... _args) -> GET_CALLBACK_RET_TYPE(pNtQueueApcthread) { std::cout << "hkNtQueueApcThread!" << std::endl; auto ret = PLH::FnCast(hkNtQueueapcThread, pNtQueueApcthread)(_args...); return ret; });
以上方案均能在MSVC、GCC、Clang下正常编译,同时保留了返回类型的强制校验能力。
内容的提问来源于stack exchange,提问作者Stephen Eckels
相关产品推荐
相关产品推荐

