6

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

根据维基百科

在 Windows 上下文中为 x64 架构编译时(无论是使用 Microsoft 还是非 Microsoft 工具),只有一种调用约定 — 此处描述的一种,因此 , stdcall, thiscall,cdeclfastcall现在都是相同的。

这会破坏我的 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
}

错误 C2995 'WrapperFC<T,Args...> wrap(T (__cdecl *)(Args...))':已定义函数模板

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

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

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

TL;博士:

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

4

2 回答 2

2

省略 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 版本错误有关,现在已修复。

于 2019-02-23T18:12:34.357 回答
1

根据文档__stdcall被忽略,所以就像你有两个__cdecl而不是每个一个。这就是为什么你不能两者兼得。

于 2021-05-07T13:28:10.103 回答