这种用例来自于想要实现一个编译时事件总线数据结构,该结构仅侦听/注册/取消注册所提供的模板参数。
从天真的实现开始,可以说我们有以下类AListener
,AEvent
,BListener
,BEvent
。
我希望我的EventBus
类看起来像这样:
class EventBus {
std::vector<AListener*> aListeners;
std::vector<BListener*> bListeners;
public:
void registerListener(AListener& listener);
void unregisterListener(AListener& listener);
void sendEvent(AEvent event);
void registerListener(BListener& listener);
void unregisterListener(BListener& listener);
void sendEvent(BEvent event);
};
有没有办法为它做模板并递归构造类?例如:
EventBus<AListener, AEvent, BListener, BEvent> eventBus;
AListener aListener;
eventBus.registerListener(aListener);
AEvent aEvent;
eventBus.sendEvent(aEvent);
BListener bListener;
eventBus.registerListener(bListener);
BEvent bEvent;
eventBus.sendEvent(bEvent);
最好为每种侦听器类型创建一个新的vector
,因为将所有指针都放在一个列表中效率不高,因为一长串无关的侦听器会浪费性能。由于事件总线将要处理很多事件,因此性能很重要。希望只迭代我们关心的对象。
最后,假设我们不会专门设置任何侦听器,因此我们在这里不必担心继承,模板列表中的所有类都被认为是最终的。
我的问题:
如何解决命名问题?虽然我认为通过方法重载在递归定义中对模板进行专门化是可以的,因为编译器有望完成理想的工作……我不确定如何处理不同的成员名称。
我的计划是从列表中取出两个成员,就像这样(注意,这是伪代码,几乎可以肯定不会编译,或者如果确实是偶然的话, ):
// Pseudocodey C++ template rough idea
template <typename Listener, typename Event, typename Args...>
class EventBus : public EventBus<Args...> {
// ???
public:
void registerListener(Listener& listener) {
// emplace back
}
void unregisterListener(Listener& listener) {
// erase remove
}
void sendEvent(Event event) {
// send for each
}
};
当然,除非有更好的方法?这可能吗?
答案 0 :(得分:2)
我将创建一个用于处理侦听器/事件的类:
template <typename Listener, Event>
class EventHandler {
std::vector<Listener*> mListeners;
public:
void registerListener(Listener& listener);
void unregisterListener(Listener& listener);
void sendEvent(Event event);
};
然后,处理所有这些的类将是:
template <typename ... Ts>
class EventBus : Ts...
{
public:
using Ts::registerListener...; // Requires C++17
using Ts::unregisterListener...; // Prior that you have to do it with recursion
using Ts::sendEvent...; // class EventBus<T, Rest...> : T, EventBus<Rest...>
};
使用方式:
EventBus<EventHandler<AListener, AEvent>, EventHandler<BListener, BEvent>> eventBus;
顺便说一句,Event
可能依赖于Listener
,因此拥有typename Listener::Event
似乎合适,并删除所有Event
模板参数。
答案 1 :(得分:1)
您可以使用元组和某些类型特征:
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
template<typename x_Event> class
t_EventTrait;
template<typename ... x_Listener> class
t_EventBus
{
private: ::std::tuple<::std::vector<x_Listener *>...> m_listeners;
public: template<typename xx_Listener> void
Register_Listener(xx_Listener & listener)
{
::std::get<::std::vector<xx_Listener *>>(m_listeners).emplace_back(&listener);
}
public: template<typename x_Event> void
Send_Event(x_Event & event)
{
for(auto p_listener: ::std::get<::std::vector<typename t_EventTrait<x_Event>::t_Listener *>>(m_listeners))
{
p_listener->On_Event(event);
}
}
};
struct t_EventA {};
struct t_ListenerA { void On_Event(t_EventA &) { ::std::cout << "handling A\n"; } };
template<> class t_EventTrait<t_EventA>{ public: using t_Listener = t_ListenerA; };
struct t_EventB {};
struct t_ListenerB { void On_Event(t_EventB &) { ::std::cout << "handling B\n"; } };
template<> class t_EventTrait<t_EventB>{ public: using t_Listener = t_ListenerB; };
int main()
{
t_EventBus<t_ListenerA, t_ListenerB> bus{};
t_ListenerA a{};
bus.Register_Listener(a);
t_EventA ea{};
bus.Send_Event(ea);
t_ListenerB b{};
bus.Register_Listener(b);
t_EventB eb{};
bus.Send_Event(eb);
return 0;
}
答案 2 :(得分:1)
在C ++ 11中,您可以使用可变参数模板
template<class... MoreEventPairs> class EventBus {};
template<class Listener, class Event>
class EventBus<Listener, Event>
{
private:
std::vector<Listener *> Listeners;
public:
EventBus() {};
~EventBus() {};
void registerListener(Listener& listener) {}; // dummy implementations here
void unregisterListener(Listener& listener) {};
void sendEvent(Event event) {};
};
template<class Listener, class Event, class ... MoreEventPairs>
class EventBus<Listener, Event, MoreEventPairs ...> : public EventBus<Listener, Event>,
public EventBus<MoreEventPairs ...>
{
public:
// these are needed so name resolution works
// one needed for each function, on both inheritance paths
using EventBus<Listener, Event>::registerListener;
using EventBus<Listener, Event>::unregisterListener;
using EventBus<Listener, Event>::sendEvent;
using EventBus<MoreEventPairs ...>::registerListener;
using EventBus<MoreEventPairs ...>::unregisterListener;
using EventBus<MoreEventPairs ...>::sendEvent;
};
// construct as
EventBus<ListenerA, EventA, ListenerB, EventB> bus;
这实际上是通过一次从参数包中剥离两种类型来实现的。如果在构造时提供奇数类型的类型(例如,省略Event
类型),它将不会编译。通过使用两参数模板的特殊化,可以对特定类型的Listener
或关联的Event
进行特殊处理。
在C ++ 11之前,您可以使用多重继承,但是需要分别构造EventBus
类。由于需要复制代码以进行扩展,因此维护起来需要付出更多的努力。
template<class Listener, class Event> class ListenerBus
{
private:
std::vector<Listener *> Listeners;
public:
ListenerBus() {};
~ListenerBus() {};
void registerListener(Listener& listener) {}; // dummy implementations here
void unregisterListener(Listener& listener) {};
void sendEvent(Event event) {};
};
// AListener, AEvent, etc are concrete classes
class EventBus : public ListenerBus<AListener, AEvent>,
public ListenerBus<BListener, BEvent>
// list other types here
{
public:
using ListenerBus<AListener, AEvent>::registerListener;
using ListenerBus<AListener, AEvent>::unregisterListener;
using ListenerBus<AListener, AEvent>::sendEvent;
using ListenerBus<BListener, BEvent>::registerListener;
using ListenerBus<BListener, BEvent>::unregisterListener;
using ListenerBus<BListener, BEvent>::sendEvent;
// need to replicate above for every base class for name resolution
};
// construct as
EventBus bus;
registerListener()
,unregisterListener()
和sendEvent()
成员函数都是非virtual
成员函数,因为您不希望它们被{{1}覆盖}(然后会受到隐藏规则的影响)。
除了假设EventBus
或Listener
类之间没有继承关系之外,以上两种方法都假设Event
和Listener
类都是不同的类型(即,没有{ {1}}类或Event
类列出了多次。如果您打破了这一假设,最可能的结果就是某些成员函数的调用将变得模棱两可。