![](/img/trans.png)
[英]Does the standard say anything about coexistence of exceptions and different calling conventions?
[英]Callable Objects with different calling conventions
目前,我正在為不同的調用約定(__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...
}
}
};
盡管這看起來像是一個可行的解決方案,但我想知道是否有一些我沒有想到的好選擇。
有任何想法嗎?
如果仍然要使用枚舉模板參數,則可以使用專門化來完成此操作。
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語句更好。
(對不起模板參數的注釋,我不太熟悉它的工作原理)
這里就是我得到了我的想法做 。
希望這可以幫助!
好了,一旦為每個調用約定定義了標簽,就可以定期使用標簽分發:
#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
這當然可以進一步發展; 標簽可能具有重新綁定的可變參數成員模板,該模板返回具有指定的適當調用約定的函數指針類型。
我想您甚至可以通過在宏中整齊地放置標簽定義來減少復制和粘貼操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.