c++ - 以 "different"调用约定作为参数的 64 位 C++ 传递函数会产生不明确的错误

标签 c++ x86-64 calling-convention stdcall cdecl

我的目标是使用 __cdecl__stdcall 调用约定轻松提取任意函数的原型(prototype)。它在 32 位中运行良好。唯一改变的是我的模板函数参数中的调用约定。

根据 Wikipedia :

When compiling for the x64 architecture in a Windows context (whether using Microsoft or non-Microsoft tools), there is only one calling convention — the one described here, so that stdcall, thiscall, cdecl, fastcall, etc., are now all one and the same.

这会破坏我的 64 位代码。即使调用约定相同,将函数作为参数传递仍然需要您使用正确的命名法。 IE。如果函数定义为 __stdcall,则必须将其传递给接受 __stdcall 的包装器。即使 __cdecl 是相同的,您仍然必须将定义为 __cdecl 的函数传递给接受 __cdecl 的包装器。

适用于 32 位的示例:

template<typename T, typename... Args>
struct WrapperSTD { typedef T(__stdcall *Functor)(Args...); };

template<typename T, typename... Args>
struct WrapperC { typedef T(*Functor)(Args...); };

template<typename T, typename... Args>
WrapperSTD<T, Args...> wrap(T(__stdcall *func)(Args...)) {
    return WrapperSTD<T, Args...>{};
}
template<typename T, typename... Args>
WrapperC<T, Args...> wrap(T(*func)(Args...)) {
    return WrapperC<T, Args...>{};
}

我的目标是能够运行,例如:

using MsgBoxProto = decltype(wrap(MessageBoxA))::Functor;

这适用于 32 位。但是,由于 __stdcall__cdecl 在 x64 中显然是相同的,因此它不能在 64 位中工作并引发错误,指出调用不明确。它还告诉我模板已经定义。直觉上,似乎我可以将带有 __cdecl 的函数传递给这个 __stdcall 函数,因为编译器认为它们是相同的。但是,这不起作用:

template<typename T, typename... Args>
struct WrapperFC { typedef T(__stdcall *Functor)(Args...); };

template<typename T, typename... Args>
WrapperFC<T, Args...> wrap(T(__stdcall *func)(Args...)) {
    return WrapperFC<T, Args...>{}; // same as below
}

template<typename T, typename... Args>
WrapperFC<T, Args...> wrap(T(__cdecl *func)(Args...)) {
    return WrapperFC<T, Args...>{}; // same as above
}

Error C2995 'WrapperFC<T,Args...> wrap(T (__cdecl *)(Args...))': function template has already been defined

如果我只保留其中一个,我就不能同时包装这两个函数:

void __cdecl foo(int i){}
void __stdcall bar(int i){}

如果编译器认为它们是相同的,为什么它要求我有不同的模板来接受不同的调用约定?当我这样做时,为什么它会完全中断并说它是模棱两可的并且已经定义了?

长话短说:

如果 64 位架构中的调用约定相同,为什么我不能将一个调用约定传递给需要另一个调用约定的函数?我该如何解决?

最佳答案

省略了 x64 的调用约定。这为我编译:

template<typename T, typename... Args>
struct WrapperC { typedef T(*Functor)(Args...); };


template<typename T, typename... Args>
WrapperC<T, Args...> wrap(T(*func)(Args...)) {
    return WrapperC<T, Args...>{};
}

void __cdecl foo(int i){}
void __stdcall bar(int i){}

int main()
{
    wrap(foo);
    wrap(bar);
    using ProtoFoo = decltype(wrap(foo))::Functor;
    using ProtoBar = decltype(wrap(bar))::Functor;
}

我知道这是一个老问题,它可能与旧的 Visual Studio 版本错误有关,现在已修复。

关于c++ - 以 "different"调用约定作为参数的 64 位 C++ 传递函数会产生不明确的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37056407/

相关文章:

c++ - 如何关闭Qt4中所有处于事件状态的选项卡(QTabWidget)

linux - 通过 ASM 代码处理系统调用

x86-64 - x86_64,i386,ia64和其他此类术语代表什么?

linux - 从未对齐 RSP 的函数调用时,glibc scanf 出现段错误

windows - Microsoft Stack 是否始终与 16 字节对齐?

c++ - 一个类可以有虚拟数据成员吗?

C++ 预处理器宏选择参数

assembly - x86-64 AT&T 指令 movq 和 movabsq 有什么区别?

c++ - 使用 thiscall 约定调用 C++ 成员函数

c++ - 将值 C#/COM 返回到 C++ 客户端?