C ++ Универсальная система событий - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь построить общую систему событий. Делегаты и мероприятия не должны знать что-либо о другом, и менеджер будет все делать.

Имея это в виду, я создал шаблонный делегат / слушатель, состоящий из указателя функции и шаблонных параметров.

class IDelegate
{
public:
    IDelegate() {};
    virtual ~IDelegate() = 0;
    virtual void exec() = 0;
};

template<class Class, typename... Args>
class Delegate : public IDelegate
{
public:
    typedef (Class::*Function)(Args);
    Delegate(Class* inst, Function func) : instance(inst), function(func) {};
    ~Delegate() { instance = nullptr };

    void exec(Args args)
    {
        instance->function(args);
    }

private:
    Class* instance;
    Function function;
};

Я сделал что-то похожее на Событии. События, состоящие из идентификатора и аргументов, которые необходимо будет передать указателю на функцию.

class IEvent
{
public:
    IEvent() {};
    virtual ~IEvent() = 0;
};

template<typename... Args>
class Event : IEvent
{
public:
    Event(ID eventID, Args args) : id(eventID), arguments(args) {};
    ~Event() = default;

    ID id;
    Args arguments;
};

Я предпочитаю использовать шаблоны в надежде на то, что не нужно вручную создавать каждый класс событий / делегатов, который может понадобиться.

Наконец, я хотел создать менеджера, который бы был синглтоном.

//EventHandler.h
#pragma once

#include <string>
#include <unordered_map>
#include <queue>

#include "Event.h"

typedef std::string ID;
typedef std::unordered_multimap<ID, void*> Listeners;

class EventHandler
{
public:
    EventHandler(const EventHandler& copy) = delete;
    ~EventHandler();

    EventHandler& operator= (const EventHandler& rhs) = delete;

    void Initialize();
    template<class Class, class TEvent>
    void Run();
    void Shutdown();

    static Listeners::iterator& Register(ID id, IDelegate* listener);
    static void Deregister(Listeners::iterator& iterator);

    static void Post(IEvent* evnt);

private:
    static  Listeners listeners;
    static std::queue<IEvent*> events;
    static EventHandler* instance;

    EventHandler() {};
    EventHandler(const EventHandler& copy);
    EventHandler& operator= (const EventHandler& rhs);
};

//EventHandler.cpp
#include "EventHandler.h"

EventHandler::~EventHandler()
{
    instance = nullptr;
}

void EventHandler::Initialize()
{
    instance = this;
}

void EventHandler::Run()
{
    //TODO: Determine the Event and cast or instantiate to the right class
    IEvent* evnt = events.front; //This should not be IEvent*, but Event<>*
    events.pop();

    listeners[evnt->id].exec(evnt->arguments); //The delegate may need to be casted too.
}

void EventHandler::Shutdown()
{
    instance = nullptr;
}

Listeners::iterator& EventHandler::Register(ID id, IDelegate* listener)
{
    Listeners::iterator iter = listeners.emplace(id, listener);
    return iter;
}

void EventHandler::Deregister(Listeners::iterator& iterator)
{
    listeners.erase(iterator);
}

void EventHandler::Post(IEvent* evnt)
{
    events.emplace(evnt);
}

Когда я сталкиваюсь с проблемами, выясняю, какое событие я на самом деле использую в Run (). Если это возможно, я хотел бы сделать это без переключателя или чего-то подобного, поскольку это побеждает использование шаблонных классов. Я подумал, что указатель функции должен быть одной и той же сигнатурой, поскольку это упростит часть кода, но сделает систему менее гибкой.

Спасибо за любую помощь.

...