5

アプリケーション用のプラグインAPIインターフェースをプログラミングしています。プラグインは、実行時に共有ライブラリとしてロードされます。次のようなインターフェイスを介してアプリケーションAPIにアクセスできます。

class IPluginAPI
{
public:
 virtual bool IsPluginsLoaded(void) = 0;
 virtual bool IsHookingEnabled(void) = 0;
 // And about 50 more methods
};

MouseClickプラグインは、特定のイベント(などMouseScroll)を「リッスン」するように要求できます。これらの関数は、合計で300を超えるさまざまなイベントを構成します。通常、私は次のようなことをします。

extern "C" void SetEventHooks(APITable& table)
{
 table.MouseClick = &PluginMouseClickEvent;
 table.MouseMove = &PluginMouseMoveEvent;
}

関数SetEventHooksはプラグインライブラリ内にあり、アプリケーションから呼び出されますが、プラグインは関数をポイントすることで目的の関数をリッスンできます。これは私が使用したい方法ではありませんが、代わりにある種の抽象化を提供したいと思います。これは私が念頭に置いていたものです:

// Interface IPluginAPI supplies a 'SetEventHook` method such as
void SetEventHook(HookID id, void * callback);

この場合HookID、すべての関数IDを含む強い型の列挙型です。

enum class HookID
{
 MouseClick,
 MouseMove,
 // ...
};

したがって、プラグインはこの関数を使用してイベントをリッスンします。

pluginAPI->SetEventHook(ID::MouseClick, &myCallback);

このアプローチの問題は、タイプセーフではなく、テンプレートを直接使用できないことです(これは実行時にライブラリとして実行されるため)。イベントごとに300の異なる関数を公開したくありません(例SetHookMouseMove(void (*)(int, int))など)。私の最後のアイデアは、プラグインにはこのタイプを安全にするユーティリティテンプレート関数があるということですが、これを簡単な方法で(ボイラープレートコードなしで)実装する方法がわかりません。

template <typename T>
SetEventHook(HookID id, T callback)
{
 if(typeof(T) == decltype(/* function type of the ID */))
  gPluginAPI->SetEventHook(id, callback);
 else static_assert("INVALID FUNCTION TYPE");
}

簡単に言えば、完全な関数テーブルや各イベントの300を超えるメソッドを公開せずに、プラグインが動的なタイプセーフな方法で特定のイベントにフックできるようにするにはどうすればよいですか?

注:簡略化のために関数ポインターを使用しましたが、使用したいstd::function

4

1 に答える 1

3

Kerrekが提案しているように、特性ポリシーを使用して問題を解決できます。基本的に、パブリックAPIの一部として、各フックIDのコールバックタイプを定義する構造を含める必要があります。

// The default traits. If you don't want to have default traits comment body
// of this type out (including curly braces).
template <HookID id>
struct CallbackTraits
{
   typedef void (*CallbackType)();
};

// Traits for MouseClick
template <>
struct CallbackTraits<HookID::MouseClick>
{
    typedef void (*CallbackType)(int);
};

// Traits for MouseDoubleClick are the same
template <>
struct CallbackTraits<HookID::MouseDoubleClick> : CallbackTraits<HookID::MouseClick> {};

// Traits for MouseMove
template <>
struct CallbackTraits<HookID::MouseMove> 
{
    typedef void (*CallbackType)(int, int);
};


// actual hooking function
template <HookID id>
void SetEventHook(typename CallbackTraits<id>::CallbackType callback)
{
    // do something with id and the callback
}

これで、このAPIを次のように使用できます。

// handlers prototypes
void MouseClicked(int button);
void MouseMoved(int x, int y);
void SomeEvent();

int main()
{
    // compiles ok
    SetEventHook<HookID::MouseClick>(MouseClicked);
    SetEventHook<HookID::MouseMove>(MouseMoved);

    // won't compile - function signature incompatible
    SetEventHook<HookID::MouseDoubleClick>(MouseMoved);

    // will compile if you left default traits body uncommented
    SetEventHook<HookID::HookWithNoTraitsDefined>(SomeEvent);
    return 0;
}

ここに作業サンプルをアップロードしました。

于 2012-07-20T20:55:48.477 に答える