Передайте базовый класс функции-делегату, который получает суперкласс - PullRequest
1 голос
/ 12 апреля 2020

У меня есть эта карта: map<IEvent, EventHandler, IEventCompare> Где EventHandler определяется как typedef void (*EventHandler)(IEvent);

IEvent - это класс, описывающий общее событие.

Теперь я хочу добавить к этой карте функция, которая получает CreationEvent, класс, который наследует IEvent. Функция определяется так:

void onCreate(CreationEvent);

Но когда я пытаюсь добавить ее на карту, я получаю ошибку компиляции

E0167 argument of type "void (Engine::IObject::*)(Engine::CreationEvent)" is incompatible with parameter of type "Engine::EventHandler" 

И если я пытаюсь чтобы явно преобразовать его в EventHandler: E0171 invalid type conversion

Я могу объявить onCreate с IEvent, но я бы хотел избежать этого, так как это потребует от меня принятия типа события, и это не очень хорошо определено.

Есть ли способ сделать то, что я пытаюсь?

IEvent:

/**
    * Represents an Event, such as collision between 2 objects or click on an object.
    */
    class IEvent
    {
    public:
        IEvent(string name) { this->name = name; };
        /**
        * Copy constructor.
        */
        IEvent(const IEvent& other) { this->name = other.name;};

        string getName() const { return this->name; };

    protected:
        string name;
    };

CreationEvent:

class CreationEvent : public IEvent
    {
    public:
        CreationEvent();

        std::chrono::time_point<std::chrono::system_clock> getCreateTime() const;

    private:
        std::chrono::time_point<std::chrono::system_clock> creationTime; /**< The creation time of this event.*/
    };

Примечания : Все внутри пространства имен Engine, а карта объявлена ​​внутри IObject.

1 Ответ

1 голос
/ 12 апреля 2020

Если я правильно понял вашу идею, вы хотите:

  • Набрать события с базовым event классом.
  • Иметь обработчики с базовым handler классом.
  • Обработчики могут получать события определенного типа.

Рассмотрим следующий пример. Для простоты я использовал std::vector вместо std::map и поместил его в класс событий.

Этот код содержит уродства, утечки и не должен использоваться в «производстве» без изменений.

#include <iostream>
#include <vector>


//***********************************************************//
struct event;

struct handler
{

};

struct event_handler
{
    event_handler(handler* receiver) : receiver_{ receiver } {}
    handler* receiver_;
    virtual void invoke(event& evt) = 0;
};

template <typename T, typename U>
struct event_handler_impl : event_handler
{
    typedef void (T::* handler_function)(U&);

    event_handler_impl(handler* receiver, handler_function function) :
        event_handler{ receiver_ },
        function_{ function } {}

    void invoke(event& evt) {
        T* typed_receiver = static_cast<T*>(receiver_);
        U& typed_event = static_cast<U&>(evt);
        (typed_receiver->*function_)(typed_event);
    }

    handler_function function_;
};

struct event
{
    void subscribe(event_handler* hdlr)
    {
        //TODO: Check. Is double added? 
        handlers_.push_back(hdlr);
    }

    void sent()
    {
        for (auto& item : handlers_)
        {
            item->invoke(*this);
        }
    }

    std::vector<event_handler*> handlers_;
};

//*****************************EXAMPLE***********************//

struct creation_event : public event
{
    int creation_id{};
};

struct bar_handler : public handler
{
    void handle_creation(creation_event& evt)
    {
        std::cout << "bar" << evt.creation_id << std::endl;
    }
};

struct foo_handler : public handler
{
    void handle_creation(creation_event& evt)
    {
        std::cout << "foo" << evt.creation_id << std::endl;
    }
};

template<typename T, typename U>
void subscribe_to_event(U& evt, T* reciver, void (T::* handler_function)(U&))
{
    evt.subscribe(new event_handler_impl<T, U>(reciver, handler_function));
}


int main()
{
    creation_event evt;
    bar_handler bar;
    foo_handler foo;


    subscribe_to_event(evt, &foo, &foo_handler::handle_creation);
    subscribe_to_event(evt, &bar, &bar_handler::handle_creation);


    evt.sent();
    evt.creation_id = 1;
    evt.sent();

    return 0;
}

Единственная сложная часть:

template <typename T, typename U>
struct event_handler_impl : event_handler

Здесь мы генерируем классы для хранения нашего типизированного «обратного вызова» и используем полиморфизм для хранения этих классов внутри нашего std :: vector, поскольку все они являются дочерними классами для обработчика.

В качестве предложения - рассмотрите возможность использования умные указатели вместо сырых указателей. Также вы можете поместить функцию void subscribe_to_even(…) в базовый класс обработчика, чтобы вы могли удалить второй параметр и просто передать "this" в event_handler_impl - new event_handler_impl<T, U>(this, handler_function)

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