At the moment I'm building functors (callable types) for the different calling conventions (__stdcall, __cdecl, __fastcall etc.). With the wrappers I'll be able to do something like this:
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;
}
At the moment I have built a wrapper for the __stdcall calling convention that is able to call any __stdcall function as long as the correct parameters are specified and the correct arguments are passed in. The class looks like this:
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)...);
}
};
With that in hand I decided to build the other wrappers but I figured that typing out the same piece of code and changing the calling convention inside of the using declaration for callable_t is more work than needed. So I wanted to find a way to build about 4 variants of callable types (for each calling convention) but couldn't find a way to do it.
So far I have tried to use an enum as a non-type template parameter like this:
template <CallingConvention Call, typename ReturnT, typename... ArgsT>
class Function
{
// ...
};
But I don't know how to iterate the Call object's type and establish the required type (I tried utilizing std::is_same/std::enable_if but that was a dead end). I also tried template specialization with code like this:
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;
};
But I wasn't thinking of the rest of the arguments (return type + parameters) so this can't work too.
So does anyway have any ideas what I can do? One method I'm thinking of is doing a switch on the non-type parameter and calling the correct one like this:
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...
}
}
};
And despite this looking like a working solution I was wondering if there was some good alternatives that I'm not thinking of.
Any ideas?