I know questions like this have been asked before, but I am yet to find an adequate answer to my specific situation.
Here it is: I am trying to build an event system, where the programmer can create events which can be broadcast by an event manager and be listened for by event listeners. Each event is derived from a common base class GameEvent
.
#pragma once
#include <string>
struct GameEvent
{
std::string name = "Game Event";
};
// Events
struct StartEvent : GameEvent
{
std::string msg = "This is the start event!";
};
struct EndEvent : GameEvent
{
std::string msg = "This is the end event";
};
struct Events
{
static const StartEvent s_StartEvent;
static const EndEvent s_EndEvent;
};
const StartEvent Events::s_StartEvent;
const EndEvent Events::s_EndEvent;
// Custom hash function for GameEvent.
struct GameEventHash
{
std::size_t operator()(const GameEvent& evt) const
{
return std::hash<std::string>()(evt.name) << 1;
}
};
// Custom equal function for GameEvent.
struct GameEventEqual
{
bool operator()(const GameEvent& t1, const GameEvent& t2) const {
return t1.name == t2.name;
}
};
To store the associations with Events to their listeners, I am trying to use an unordered map of GameEvent
s to vectors of std::function
s. This can be seen as a static member of the EventManager
class.
#pragma once
#include "events.h"
#include <functional>
#include <unordered_map>
#include <vector>
struct EventManager
{
template<typename T>
static void AddListener(std::function<void(T)> listener);
static void Broadcast(GameEvent evt);
private:
static std::unordered_map<GameEvent, std::vector<std::function<void(GameEvent)>>, GameEventHash, GameEventEqual> s_Events;
};
inline void EventManager::Broadcast(GameEvent evt)
{
for (std::function<void(GameEvent)> listener : s_Events[evt])
{
listener(evt);
}
}
template<typename T>
void EventManager::AddListener(std::function<void(T)> listener)
{
T evt;
// Add function to map of events to listeners.
auto iter = s_Events.find(evt);
// If event already has listeners
if (iter != s_Events.end())
{
// Add to the vector of listeners.
std::vector<std::function<void(GameEvent)>>& listeners = s_Events[evt];
listeners.push_back(listener);
}
else
{
// Insert new vector with listener.
s_Events[evt] = std::vector<std::function<void(GameEvent)>>{listener};
}
}
std::unordered_map<GameEvent, std::vector<std::function<void(GameEvent)>>, GameEventHash, GameEventEqual> EventManager::s_Events;
The problem I have is with the AddListener
method of the EventManager
class. Because the vector of the unordered map of events takes functions of type GameEvent
, trying to use any other type in the template causes errors. The errors in question are:
EventSystem\EventSystem\event-manager.h(40,12): error C2664: 'void std::vector<std::function<void (GameEvent)>,std::allocator<std::function<void (GameEvent)>>>::push_back(const _Ty &)': cannot convert argument 1 from 'std::function<void (StartEvent)>' to 'const _Ty &'
and
EventSystem\EventSystem\event-manager.h(45,62): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'std::vector<std::function<void (GameEvent)>,std::allocator<std::function<void (GameEvent)>>>'
I understand my problem, but I do not understand how to fix it. Could someone provide solutions or alternative suggestions?