Передача 64-битных функций C++ с "различными" соглашениями о вызовах в качестве параметров дает неоднозначную ошибку

Моя цель - легко извлечь прототип произвольной функции с соглашениями о вызовах __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-битных архитектурах идентичны, почему я не могу передать одно соглашение о вызовах в функцию, которая ожидает другое? И как я могу это исправить?

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, которая сейчас исправлена.

Согласно документации , __stdcall игнорируется, так что это будет похоже на то, что у вас есть два __cdeclвместо одного каждого. Вот почему у вас не может быть обоих.

Другие вопросы по тегам