目前我正在为不同的调用约定(__stdcall,__ cdecl,__ fastcall等)构建仿函数(可调用类型)。使用包装器,我可以做这样的事情:
void __stdcall foo(int arg)
{
std::printf("arg: %i\n", arg);
}
int main(int, char**)
{
Function<void, int> v{foo};
v(1337);
return EXIT_SUCCESS;
}
目前我为__stdcall调用约定构建了一个包装器,只要指定了正确的参数并传入了正确的参数,就可以调用任何__stdcall函数。该类看起来像这样:
template <typename ReturnT, typename... Args>
class Function
{
// NOTE: This version of my callable types
// only supports the __stdcall calling
// convention. I need support for __cdecl,
// __fastcall and also __thiscall.
using return_t = ReturnT;
using callable_t = return_t(__stdcall*)(Args...);
private:
callable_t mCallable;
public:
template <typename FuncT>
Function(FuncT const &func) :
mCallable(func)
{
;
}
void operator()(Args&&... args)
{
mCallable(std::forward<Args>(args)...);
}
};
有了这个,我决定构建其他包装器,但我认为键入相同的代码片段并更改callable_t的using声明中的调用约定比需要更多的工作。所以我想找到一种方法来构建大约4种可调用类型的变体(对于每种调用约定),但是找不到一种方法。
到目前为止,我尝试将枚举用作非类型模板参数,如下所示:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
// ...
};
但我不知道如何迭代Call对象的类型并建立所需的类型(我尝试使用std :: is_same / std :: enable_if但这是一个死胡同)。我还尝试使用这样的代码进行模板专业化:
struct StdcallT { ; };
struct CdeclT { ; };
struct FastcallT { ; };
template <typename CallT>
struct BaseT { };
template <> struct BaseT<StdcallT> { using CallableT = void(__stdcall*)(); };
template <> struct BaseT<CdeclT> { using CallableT = void(__cdecl*)(); };
template <> struct BaseT<FastcallT> { using CallableT = void(__fastcall*)(); };
template <typename CallT>
class Function
{
using CallableT = typename BaseT<CallT>::CallableT;
};
但是我没有考虑其余的参数(返回类型+参数),所以这也不行。
无论如何,我有什么想法可以做什么?我想到的一种方法是切换非类型参数并调用正确的参数,如下所示:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
void operator()(ArgsT&&... args)
{
switch(Call)
{
case CallingConvention::Cdecl:
// Call a __cdecl version
break;
case CallingConvention::Stdcall:
// Call an __stdcall version
break;
// And so on...
}
}
};
尽管这看起来像一个有效的解决方案,但我想知道是否有一些我没想过的好选择。
有什么想法吗?
答案 0 :(得分:1)
如果您仍想使用枚举模板参数,则可以使用特化来完成此操作。
enum CallingConvention { __stdcall, ... };
template < CallingConvention Call >
struct implement {
template</* Template arguments for call method */>
static ReturnT call(/* arguments to run method */);
};
template < CallingConvention Call, typename ReturnT, typename... ArgsT >
class Function
{
// ...
template <typename FuncT>
Function(FuncT const &func) : mCallable(func), mCall(Call) {}
CallingConvention const mCall;
return_t operator()(ArgsT&&... args) {
return implement<Call>::call</* Template arguments for call method */>(/* arguments to run method */);
};
};
template < >
struct implement< __stdcall > {
template</* Template arguments for call method */>
static ReturnT call(/* arguments to run method */) {
// Special implementation...
}
};
这比switch语句好。
(抱歉对模板参数的评论我不太熟悉它是如何工作的)
以下是我对did的想法。
希望这有帮助!
答案 1 :(得分:1)
一旦为每个调用约定定义了标记,就可以定期使用标记调度:
#include <iostream>
#include <type_traits>
struct cdecl_tag { typedef void ( __attribute__((cdecl)) *type)(); };
struct stdcall_tag { typedef void ( __attribute__((stdcall)) *type)(); };
struct fastcall_tag { typedef void ( __attribute__((fastcall)) *type)(); };
constexpr void get_func_calling_convention_tag () {};
template<typename R, typename... Args>
constexpr cdecl_tag
get_func_calling_convention_tag (R (__attribute__((cdecl)) *)(Args...))
{ return {}; }
template<typename R, typename... Args>
constexpr stdcall_tag
get_func_calling_convention_tag (R (__attribute__((stdcall)) *)(Args...))
{ return {}; }
template<typename R, typename... Args>
constexpr fastcall_tag
get_func_calling_convention_tag (R (__attribute__((fastcall)) *)(Args...))
{ return {}; }
#define CALLING_CONVENTION_TAG(func) \
decltype(get_func_calling_convention_tag(&func))
int __attribute__((cdecl)) foo (char) { return 0; }
long __attribute__((stdcall)) bar (int) { return 0; }
int main()
{
std::cout << std::is_same<CALLING_CONVENTION_TAG(foo),
cdecl_tag>::value << '\n'
<< std::is_same<CALLING_CONVENTION_TAG(bar),
stdcall_tag>::value << '\n'
<< std::is_same<CALLING_CONVENTION_TAG(foo),
CALLING_CONVENTION_TAG(bar)>::value << std::endl;
return 0;
}
在行动中看到它:http://ideone.com/HSZztX
这当然可以进一步发展;标签可以有一个重新绑定可变参数成员模板,该模板返回一个具有指定的适当调用约定的函数指针类型。
我想你甚至可以通过在宏中整齐地标记定义来减少复制和粘贴。