2015-12-29 26 views
8

現在、私は異なる呼び出し規約(__stdcall、__cdecl、__fastcallなど)のためのファンクタ(呼び出し可能なタイプ)を構築しています。正しいパラメータが指定されているように私がいる限り任意の__stdcall関数を呼び出すことが可能である__stdcall呼び出し規約のラッパーを構築している現時点で異なる呼び出し規約を持つ呼び出し可能オブジェクト

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; 
} 

:ラッパーで、私はこのような何かを行うことができるでしょうそして、正しい引数が渡されたクラスは、次のようになります。それに

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の宣言を使用することは、必要以上に多くの作業になります。だから私は約4種類の呼び出し可能な型(各呼び出し規約のために)を構築する方法を見出したいが、それを行う方法を見つけることができなかった。

これまでのところ、私はこのような非型テンプレートパラメータとして列挙型を使用しようとしました:

template <CallingConvention Call, typename ReturnT, typename... ArgsT> 
class Function 
{ 
    // ... 
}; 

しかし、私は、Callオブジェクトの型を反復処理し、必要な型を確立する方法がわからない(I 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

なぜホイールを改造するのですか? 'std :: function'は、あなたが指定した引数で呼び出し可能である限り、呼び出し規約にかかわらず、すでに関数ポインタまたは関数子を格納することができます。それはあなたのためには機能しないのでしょうか、それともあなたのカスタム実装を始めるのにいくつかの具体的な欠点がありますか? – hvd

+0

@hvd "glCreateProgram"や "glCreateShader"のようなstd :: unordered_map の文字列をopengl32.dllからシンボルアドレスにロードしたとします。 args/returnsがない場合はvoidを使用し、バイナリ互換性があるのでargs/no argsの場合はstd :: tupleを使用するテンプレート関数function_cast を作成したいとします。 std:functionはstdcallでは機能しません。したがって、コンベンション呼び出しの呼び出しを切り替えるためのダミー呼び出し規約では、ランタイムが必要です。 – Dmitry

+0

@Dmitry "std:関数はstdcallで機能しません" - GCC(GCC 5まで)の古いバージョンは、名前のマングリングで呼び出し規約を無視し、リンカエラーを引き起こしましたが、これは2015年中頃に修正されました。少なくともそのコンパイラ以降、デフォルト以外の呼び出し規約の 'std :: function'に問題があります。他のコンパイラも同じように動作するはずです*。もしそうでなければ、それがどれであるかについての詳細を教えてもらえますか? – hvd

答えて

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文より優れています。私は何のI didためのアイデアを得たところ

(テンプレート引数のコメントについては申し訳ありません、私はそれがどのように動作するかと非常に慣れていないよ)

ここです。


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
。タグには、適切な呼び出し規約が指定された関数ポインタ型を返すリバインドバリアントメンバテンプレートがあります。

タグ定義をマクロできれいにすることで、コピーと貼り付けを減らすこともできます。

関連する問題