使用模板类型为该模板类型生成唯一的成员名称

时间:2019-01-10 06:44:34

标签: c++ templates c++17

这种用例来自于想要实现一个编译时事件总线数据结构,该结构仅侦听/注册/取消注册所提供的模板参数。

从天真的实现开始,可以说我们有以下类AListenerAEventBListenerBEvent

我希望我的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
    }
};

当然,除非有更好的方法?这可能吗?

3 个答案:

答案 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;   
}

online compiler

答案 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}覆盖}(然后会受到隐藏规则的影响)。

除了假设EventBusListener类之间没有继承关系之外,以上两种方法都假设EventListener类都是不同的类型(即,没有{ {1}}类或Event类列出了多次。如果您打破了这一假设,最可能的结果就是某些成员函数的调用将变得模棱两可。