Обратные вызовы C ++ для менеджера событий с использованием std :: function и std: bind с производными классами в качестве параметров - PullRequest
0 голосов
/ 12 января 2020

У меня есть следующий менеджер событий:

AppEventManager.h

#pragma once

#define GF_BIND_FN(fn) std::bind(&fn, this, std::placeholders::_1)

struct Event
{
public:
    enum EventType
    {
        AppQuit,
        AppBackground,

        WindowResize,
        WindowGainFocus,
        WindowLostFocus

    };

    EventType type = EventType::AppQuit;

    Event(EventType type) : type(type) {}
};

struct WindowResizedEvent : public Event
{
public:
    int width = 0;
    int height = 0;

    WindowResizedEvent(int width, int height) : width(width), height(height), Event(Event::EventType::WindowResize) {}
};

typedef std::function<void(Event&)> Callback;


class AppEventManager
{
public:

    static void AddListener(Event::EventType type, Callback c);

    template <typename T>
    static void TriggerEvent(Event& event);

private:

    static std::map<Event::EventType, std::vector<Callback>> listeners;
};


template<typename T>
inline void AppEventManager::TriggerEvent(Event& event)
{
    std::map<Event::EventType, std::vector<Callback>>::iterator it = listeners.find(event.type);

    if (it != listeners.end())
    {
        for (auto& callback : it->second)
        {
            callback(static_cast<T&>(event));
        }
    }
}

AppEventManager. cpp

#include "AppEventManager.h"

std::map<Event::EventType, std::vector<Callback>> AppEventManager::listeners = std::map<Event::EventType, std::vector<Callback>>();

// Store callback function for each event type
void AppEventManager::AddListener(Event::EventType type, Callback c)
{
    std::map<Event::EventType, std::vector<Callback>>::iterator it = listeners.find(type);

    if (it != listeners.end())
    {
        for (auto& callback : it->second)
        {
            // Check if callback exist
            if (callback.target_type().hash_code() == c.target_type().hash_code())
            {
                return;
            }
        }
    }

    listeners[type].emplace_back(c);
}

Чтобы добавить слушателя:

Window::Window()
{
    AppEventManager::AddListener(Event::EventType::WindowResize, GF_BIND_FN(Window::WindowResized));
}

void Window::WindowResized(Event& event)
{
    if (event.type == Event::EventType::WindowResize)
    {
        WindowResizedEvent e = reinterpret_cast<WindowResizedEvent&>(event);
        windowWidth = e.width;
        windowHeight = e.height;
    }
}

Для запуска события:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_SIZE:
    {
        WindowResizedEvent event(LOWORD(lParam), HIWORD(lParam)); <---
        AppEventManager::TriggerEvent<WindowResizedEvent>(event); <---
    }
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

Этот код работает, но, как вы видите в void Window::WindowResized(Event& event) Мне нужно привести событие к производное WindowResizedEvent.

То, чего я хочу добиться, это вызвать void Window::WindowResized(Event& event) напрямую с параметром WindowResizedEvent: void Window::WindowResized(WindowResizedEvent & event), но теперь это невозможно, поскольку typedef std::function<void(Event&)> Callback; требует, чтобы параметр был Event, а не был получен из Event .

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

Если вы знаете совершенно другой способ добиться этого, это тоже нормально.

1 Ответ

1 голос
/ 12 января 2020

Вы можете иметь отдельные векторы для каждого типа. Доступ к ним осуществляется через функции шаблонов.

class AppEventManager
{
public:

    template <typename T>
    static void AddListener(std::function<void(T&)> callback) {
        get_listeners<T>().push_back(std::move(callback));
    }

    template <typename T>
    static void TriggerEvent(T& event) {
        for (auto& listener : get_listeners<T>()) {
            listener(event);
        }
    }

private:

    template <typename T>
    static std::vector<std::function<void(T&)>>& get_listeners() {
        static std::vector<std::function<void(T&)>> listeners;
        return listeners;
    }
};

И используется с типом напрямую вместо перечисления.

Window::Window()
{
    AppEventManager::AddListener<WindowResizedEvent>(GF_BIND_FN(Window::WindowResized));
}

В качестве примечания рекомендуется использовать лямбды вместо std::bind.

Window::Window()
{
    AppEventManager::AddListener<WindowResizedEvent>([&](WindowResizedEvent& event) { WindowResized(event); });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...