Ell*_*ink 5 c++ gcc shared-libraries c++11
我正在为应用程序编写插件API接口.插件在运行时作为共享库加载.他们可以通过界面访问应用程序API,例如:
class IPluginAPI
{
public:
virtual bool IsPluginsLoaded(void) = 0;
virtual bool IsHookingEnabled(void) = 0;
// And about 50 more methods
};
Run Code Online (Sandbox Code Playgroud)
插件可以请求"监听"某些事件(例如MouseClick,MouseScroll等等).这些功能共计300多个不同的事件.通常我会做这样的事情:
extern "C" void SetEventHooks(APITable& table)
{
table.MouseClick = &PluginMouseClickEvent;
table.MouseMove = &PluginMouseMoveEvent;
}
Run Code Online (Sandbox Code Playgroud)
虽然SetEventHooks函数驻留在插件库中并且从应用程序调用,但插件可以通过指向其函数来监听感兴趣的函数.这不是我想要使用的方法,但我想提供某种抽象.这就是我的想法:
// Interface IPluginAPI supplies a 'SetEventHook` method such as
void SetEventHook(HookID id, void * callback);
Run Code Online (Sandbox Code Playgroud)
在这种情况下,HookID是一个包含所有函数ID的强类型枚举:
enum class HookID
{
MouseClick,
MouseMove,
// ...
};
Run Code Online (Sandbox Code Playgroud)
所以插件会使用这个函数来监听事件:
pluginAPI->SetEventHook(ID::MouseClick, &myCallback);
Run Code Online (Sandbox Code Playgroud)
这种方法的问题在于它不是类型安全的,我不能直接使用模板(因为这在运行时作为库完成).我不想为每个事件公开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");
}
Run Code Online (Sandbox Code Playgroud)
简而言之; 如何在不向每个事件公开完整的函数表和/或> 300方法的情况下,使我的插件以动态类型安全的方式挂钩到某些事件?
注意:我使用函数指针进行简化,但我想使用 std::function
正如 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
}
Run Code Online (Sandbox Code Playgroud)
现在您可以通过以下方式使用此 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;
}
Run Code Online (Sandbox Code Playgroud)
我在这里上传了一个工作示例。