64位C ++传递具有“不同”调用约定作为参数的函数会产生不明确的错误

问题描述 投票:6回答:1

我的目标是使用__cdecl和__stdcall调用约定轻松地提取任意函数的原型。它在32位工作正常。唯一改变的是模板函数参数中的调用约定。

根据Wikipedia的说法:“在Windows上下文中编译x64架构时(无论是使用Microsoft还是非Microsoft工具),只有一个调用约定 - 这里描述的那个,所以stdcall,thiscall,cdecl,fastcall等。 ,现在都是一样的。“

这打破了64位的代码。即使调用约定相同,将函数作为参数传递仍然需要使用正确的命名法。 I.E.如果函数定义为__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 wrap(T(__ cdecl *)(Args ...))':函数模板已经定义

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

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

如果编译器看到它们是相同的,为什么它需要我有不同的模板来接受不同的调用约定?当我这样做时,为什么它完全破裂并说它含糊不清并且已经定义了?

TL; DR:如果64位体系结构中的调用约定是相同的,为什么我不能将一个调用约定传递给期望另一个的函数?我该如何解决?

c++ 64bit calling-convention stdcall cdecl
1个回答
1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.