创建"发布商 - >调度员 - >订阅者"模式事件系统?

时间:2017-11-03 05:06:36

标签: c++ c++11 templates events dispatch

编辑:TL; DR

我想我的主要问题是我不知道如何存储一个函数列表,这些函数都接受一个参数,其中参数类型在每个函数之间是不同的,但总是从EventBase扩展,以便稍后调用。

即:EventChild从EventBase扩展而来。带签名的函数

<void (EventChild&)> 

不适合

类型的变量
std::function<void(EventBase&)>

我如何存储这样的函数,知道用户每次创建从EventBase类扩展的新事件时都不应该修改存储它们的类?

注意:我之前被告知我可以使用dynamic_cast来完成此任务。我一直在努力做到这一点,但它还没有奏效。我想,为了工作,我必须以某种方式使用指针,但我对C ++是新的,我不知道该怎么做。也许这应该是起点?

我一直遇到的动态转换指针的一个问题是,我可以转换类型的指针:

(Subbscriber*)(getDemoEvent(EventDemo&)

输入:

void(EventBase&)

或类似的东西。 (现在不在我的电脑上试试) 这显然是一个仅限于成员函数的问题,我认为。

我最近在这里发布了一个问题,旨在解决基于&#34;发布商 - &gt; Dispatcher-&gt; Subscriber&#34;的C ++事件系统的问题。图案。我不知道这个模式的确切名称,但是我听说它是​​Observer模式的一个变种,添加了#34;中间人。&#34;

我一直试图让这个系统工作一段时间,我完全陷入困境。在前一个问题的评论中建议,对于我想要完成的事情,我的程序布局是不正确的。这很可能就是这种情况,因为我一直在研究其他事件系统,这些事件系统在尝试修改它们之后与我无关,因为它们是无意的。所以我想我会描述我追求的是什么,并且问一下更为笼统的问题&#34;你将如何构建和创建它?&#34;

以下是我对系统应如何布局以及如何在一个基本示例中运行的总体思路:

从5个不同文件的概念开始(加上标题,也许还有一些子类):

main.cpp
dispatcher.cpp
publisher.cpp
subscriber.cpp
eventbase.cpp

发布商和订阅者可以是任何内容,它们仅作为示例。 第一项业务是创建Dispatcher类的实例。 接下来,我们创建发布者/订阅者类的实例。这两个类可以是同一个文件的一部分,不同的文件,每个的倍数,或者不是事件,而是简单的自由函数。为了简单和测试,它们是两个彼此不了解的独立类。 创建这两个类时,应该将它们传递给我们的调度程序实例的引用或指针。 这很容易。现在让我们了解您应该如何使用该系统。

系统用户应该能够创建一个继承自EventBase类的类。理想情况下,不应要求从基类重写变量或函数。 让我们说我们已经创建了一个名为EventDemo的新事件类,其中包含一个公共const char * demoString =&#34;我是一个演示事件&#34 ;;。

从我们的订阅者类中,我们应该能够告诉我们的调度员我们想要收听和接收一些事件。这样做的语法应该尽可能简单。 让我们在订阅者中创建一个如下所示的成员函数:

void Subscriber::getDemoEvent(const EventDemo &ev) {
    std::cout << ev.demoString;
} 

现在我们需要一种方法将该成员函数绑定到我们的调度程序。我们应该在构造函数中做到这一点。让我们说,我们在创建订阅者时传递给我们的调度员的提法只是调用了“调度员”。 订阅事件的语法应如下所示:

dispatcher->subscribe("EventToSubTo", &getDemoEvent);

现在因为我们正在尝试传递一个成员函数,所以这可能是不可能的,但它适用于自由函数。 对于成员函数,我们可能需要并覆盖如下所示:

dispatcher->subscribe("EventToSubTo", &Subscriber::getDemoEvent, this);

我们使用&#39;这个&#39;因为我们在订阅者构造函数中。否则,我们可以使用对订户的引用。 请注意,我只是使用字符串(或c ++术语中的const char *)作为我的&#34;事件键&#34;。这是有目的的,因此您可以使用相同的事件&#34;键入&#34;多个事件。 I.E:EventDemo()可以发送到密钥&#34; Event1&#34;和&#34; Event2&#34;。

现在我们需要发送一个事件。这可以在我们调度我们的调度员的任何地方完成。在这种情况下,我们的发布者类中的某个地方。 语法应该类似于发送我们的EventDemo:

dispatcher->emit("EventToSubTo", EventDemo());

超级简单。值得注意的是,我们应该能够通过它的构造函数为我们的事件分配数据,甚至模板化事件。这两种情况仅在用户创建的事件支持时才有效。 在这种情况下,上面的代码看起来像这样:

dispatcher->emit("EventToSubTo", EventDemo(42));

dispatcher->emit("EventToSubTo", EventDemo<float>(3.14159f));

用户可以创建一个成员函数来检索数据。

好的,所以,所有这一切看起来都很简单,事实上,它只是一个小问题。已有系统将函数存储在具有类型的地图中。 其中存在的问题是...... 我们可以存储我们的侦听器函数,只要它们接受一种EventBase作为它们的参数。然后我们必须输入cast参数到我们所追求的事件类型。这并不是太难做到,但这不是重点。关键是它可以更好。 之前提出的另一个解决方案是为每种类型的事件提供单独的地图或矢量。这也不错,但要求用户修改调度程序类(当这是一个库时很难做到),或者以某种方式告诉调度员创建这组地图&#34; 34;在编译时。这也会使事件成为一场噩梦。

所以,过于笼统的问题:我们如何做到这一点?

对于看似简单的事情来说,这可能是一个非常冗长的解释,但也许有人会不会不知道它。

我很想听听有关这方面的想法。核心思想是我不希望2个传播者(发布者和订阅者)必须知道彼此之间的任何事情(没有指针或引用),但仍然能够将任意数据从一个传递到另一个。我见过的大多数实现(信号和插槽)都需要相互引用。创造一个&#34;中间人&#34;界面感觉更灵活。

感谢您的时间。

参考我上一期的问题,以及我目前所拥有的代码示例:     Store a function with arbitrary arguments and placeholders in a class and call it later

我有更多可以发布的样本,但我认为很可能系统的结构必须改变。等着听到想法!

0 个答案:

没有答案