繁体   English   中英

具有不同调用约定的可调用对象

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM