I am making a C++ sdl based game engine, currently doing the EventManager and the event system, this is what I have now:

Here’s a broad overview of how the classes and their methods could be structured within your event-driven system. This structure is designed to maintain clarity, flexibility, and separation of concerns while leveraging C++ features for efficient and effective event management.

### EventManager

class EventManager {
public:
    void Subscribe(EventType type, std::function<void(std::shared_ptr<Event>)> handler);
    void Publish(std::shared_ptr<Event> event);
    void Update(); // Processes the event queue and dispatches events

private:
    std::mutex queueMutex;
    std::queue<std::shared_ptr<Event>> eventQueue;
    std::map<EventType, std::vector<std::function<void(std::shared_ptr<Event>)>>> subscribers;
};


In this setup:

- Subscribe allows different parts of the game engine (e.g., InputManager) to subscribe to specific event types with a callback function.
- Publish adds events to the queue. In a real-time game, you’d likely call this method in response to SDL events or other input sources.
- Update is called every game loop iteration to dispatch and clear processed events. It ensures thread-safe access to the event queue.

### Event Base Class and Subclasses

class Event {
public:
    virtual ~Event() = default;
    virtual EventType GetType() const = 0;
    // Common event data and methods
protected:
    int priority;
};

class MouseButtonEvent : public Event {
public:
    EventType GetType() const override { return EventType::MouseButton; }
    // Mouse button event-specific data and methods
};

class KeyboardEvent : public Event {
public:
    EventType GetType() const override { return EventType::Keyboard; }
    // Keyboard event-specific data and methods
};

// Additional Event subclasses follow the same pattern.

In this structure:

- Each Event subclass implements GetType(), returning its specific EventType. It allows EventManager to correctly dispatch the event to subscribers interested in that type.
- You can extend the hierarchy with more event types as needed, following the same pattern.

### Usage with InputManager (Consumer Example)

class InputManager {
public:
    InputManager(EventManager& eventManager) {
        // Subscribe to specific event types
        eventManager.Subscribe(EventType::Keyboard, [this](std::shared_ptr<Event> event){ this->HandleKeyboardEvent(std::static_pointer_cast<KeyboardEvent>(event)); });
    }

    void HandleKeyboardEvent(std::shared_ptr<KeyboardEvent> event) {
        // Process the keyboard event
    }

    // Other event handling methods
};


In this example:

- InputManager subscribes to events by providing a lambda that wraps its handling method. For events it is interested in, it casts the Event pointer to the correct type using std::static_pointer_cast.
- This pattern can be repeated for other managers or game components that need to respond to specific events.

Now my question is, is there a way to change the EventType here, that it would be an Enum, to an alternative that uses " an Event or any of its sub-classes", so giving flexibility to the user to create their own event deriving from Event, and also EventManager supporting those events?