My goal is to easily extract a prototype of an arbitrary function with both the __cdecl
and __stdcall
calling conventions. It works fine in 32-bit. The only thing that's changing is the calling convention in my template function parameters.
According to 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.
This breaks my code in 64-bit. Even though the calling conventions are the same, passing the functions as a parameter still requires that you use the correct nomenclature. I.E. if a function is defined as __stdcall
, you must pass it into a wrapper that accepts __stdcall
. Even though __cdecl
is identical, you still must pass functions defined as __cdecl
to a wrapper that accepts __cdecl
.
Example that works in 32-bit:
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...>{};
}
My goal was to be able to run, for example:
using MsgBoxProto = decltype(wrap(MessageBoxA))::Functor;
This is fine for 32-bit. However, since __stdcall
and __cdecl
are apparently identical in x64, it will not work in 64-bit and raises an error saying that the calls are Ambiguous. It also tells me that the template has already been defined. Intuitively, it seems like I would be able to pass functions with __cdecl
into this __stdcall
function since the compiler sees them as identical. However, this does not work:
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
If I only keep one of them, I cannot wrap both of these functions at the same time:
void __cdecl foo(int i){}
void __stdcall bar(int i){}
If the compiler sees them as the same, why does it require me to have different templates for accepting the different calling conventions? And when I do, why does it completely break and say it's ambiguous and already defined?
TL;DR:
If the calling conventions in 64-bit architectures are identical, why is it that I cannot pass one calling convention into a function that expects another? And how can I fix it?
char
vsunsigned char
(orsigned char
; depends on your platform) withint
vssigned int
. Similarly,int
andlong
are distinct types, even if both are 32-bit two's complement. You need to mentally separate definition from implementation. But I have no idea whether any of this is relevant to your question :o) – Hothouse