I've been playing around and trying to get to grips with some of the new C++ features and decided to do a little project using them, I really like lambdas I think they are a really great addition. However I'm having a few issues on coming up with a way to use them in an event system.
Ideally I'm trying to design a system where components can register themselves to listen for certain events and when an event is triggered these registered components are notified. My first implementation of it worked well, here's what I had:
class EventManager
{
public:
void AddListener(std::string name, std::function<void(void)> func)
{
events[name].push_back(func);
}
void TriggerEvent(std::string name)
{
//Error checking here
for (auto it = events[name].begin(); it != events[name].end(); it++)
{
(*it)();
}
}
private:
std::map<std::string, std::vector<std::function<void(void)>>> events;
};
int main()
{
EventManager ev;
auto func = []() {std::cout << "Hello world!" << std::endl; };
ev.AddListener("hello", func);
ev.AddListener("hello", func);
ev.TriggerEvent("hello");
system("PAUSE");
return 0;
}
As I said this works quite well but it's fairly basic.. Now my problem that I'm trying to solve is I'd like to be able to pass around variables. For example on a "WindowResize" event I'd like to be able to pass the new width and height to each function but still be able to maintain them in one class.
My second attempt used polymorphism to remove the limit of only being able to store std::function<void(void)>'s which looks like this:
class IEvent
{
public:
virtual void dummy() = 0;
};
template<class T>
class Event : public IEvent
{
public:
Event(T t) : func(t) { }
virtual void dummy()
{
}
T func;
};
class EventManager
{
public:
void AddListener(std::string name, IEvent* func)
{
events[name].push_back(func);
}
//This is incorrect.
template<class... Args>
void TriggerEvent(std::string name, Args... args)
{
//Error checking here
for (auto it = events[name].begin(); it != events[name].end(); it++)
{
//Call function here
}
}
private:
std::map<std::string, std::vector<IEvent*>> events;
};
int main()
{
EventManager ev;
Event<std::function<void(int, int)>> something([](int x, int y) {std::cout << "X: " << x << "Y:" << y << std::endl; });
ev.AddListener("hello", &something);
ev.AddListener("hello", &something);
ev.TriggerEvent("hello");
system("PAUSE");
return 0;
}
But I'm unable to figure out how to actually trigger the event and pass in the correct parameters. When I got to this point I realized that I can't use the variadic template in that way and that also events with different function types could be stored in the same vector which causes horrible run time errors when called... If anyone could point me in the right direction on how to "properly" do this that'd be great
Thanks.