我正在创建一个应用程序,目前可以通过从接口类(称为AppInterface)继承并由Qt的插件加载器系统加载的几个插件进行扩展。可以使用dll为每个插件提供被视为核心的类的全局静态实例。
现在,要在插件之间交换数据,我计划在核心中创建2个函数,一个函数将侦听器函数指针存储在映射中的键下,另一个函数调用存储的函数 在键下,并带有参数。
std::map<char*,std::list<void*> > listenerMap;
template<typename... Args, typename T>
void listen(char* key, T *inst, void (T::*listenerSlot)(Args...))
{
get the functor list with key from the listener map and append the functor.
}
template<typename ...Args>
void broadcast (char* key, Args... args)
{
get the list with key from the listener map and invoke all the functors with the given arguments.
}
我还没有找到一种方法来存储仿函数及其类对象,并在以后调用它们。函子参数不是固定的,它们会因函数而异,除非它们在侦听器映射中的同一键下。
是否需要更改广播和侦听功能的结构,或者有更好的主意来调用存储的成员函数?
还是我可以使用每个插件都继承的AppInterface?
NB:出于某些原因,我决定不使用Qt信号槽系统,因为我希望AppInterface成为基类而不是QObject。
答案 0 :(得分:1)
似乎您想依靠名称来知道要传递的参数,因此插件应该知道其他插件如何命名其方法以及要传递的参数...
然后,提供一种检索插件的方法似乎更简单:
AppInterface* GetPlugin(const char* name);
,直到插件将dynamic_cast
插入所需的插件,并直接调用其上的任何方法。例如:
struct SomePlugin : AppInterface
{
// ...
void print();
int foo(int n);
};
struct MyPlugin : AppInterface
{
// ...
void bar() {
auto* plugin = dynamic_cast<SomePlugIn>(GetPlugin("SomePlugIn"));
if (plugin) {
plugin->print();
int n = plugin->foo(42);
// ...
}
}
};
使用static const char* Plugin::Name
作为约定,您可以直接在函数中提供dynamic_cast:
template <typename Plugin>
Plugin* GetPlugin() { return dynamic_cast<Plugin>(GetPlugin(Plugin::Name)); }
如果您确实想保留您的接口来注册功能并调用它们,则可以使用std::any
:/
std::map<std::string, std::any> listenerMap;
template<typename Sig>
void register(const char* key, std::function<Sig> f);
{
listenerMap[key] = f;
}
template<typename Sig, typename ...Args>
void broadcast(const char* key, Args&& ... args)
{
auto it = listenerMap.find(key);
if (it != listenerMap.end()) {
auto* f = std::any_cast<std::function<Sig>>(&it->second);
if (f) {
(*f)(std::forward<Args>(args)...);
}
}
}
用法类似于:
struct MyPlugin : AppInterface
{
// ...
void print() const;
void foo(int) const;
void my_register() {
register<void()>("my_print", [this](){ this->print(); });
register<void(int)>("foo", [this](int n){ this->foo(n); });
}
void call_me() {
broadcast<void()>("my_print");
broadcast<void(int)>("foo", 42);
}
};
您应该将Sig
传递给broadcast
,并且为了安全起见,请勿从Args
中扣除。
传递std::string&
而不是const std::string&
或const char*
而不是std::string
会使更改std::function<void(const std::string&)>
到std::function<void(std::string&)>
,因此std::any_cast
会失败