Моя цель — легко извлечь прототип произвольной функции с соглашениями о вызовах __cdecl и __stdcall. Он отлично работает в 32-битной. Единственное, что меняется, это соглашение о вызовах в параметрах моей функции шаблона.
В соответствии с Википедия:
«При компиляции для архитектуры x64 в контексте Windows (с использованием инструментов Microsoft или сторонних разработчиков) существует только одно соглашение о вызовах — описанное здесь, так что теперь все stdcall, thiscall, cdecl, fastcall и т.д. одно и то же.»
Это нарушает мой код в 64-битном. Несмотря на то, что соглашения о вызовах одинаковы, передача функций в качестве параметра по-прежнему требует правильной номенклатуры. И.Е. если функция определена как __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-разрядных архитектурах идентичны, почему я не могу передать одно соглашение о вызовах в функцию, которая ожидает другое? И как я могу это исправить?
Задача ещё не решена.
Других решений пока нет …