Event system

Hello,

As an exercise i'm developing a game engine on top of the SFML framework.
I have a statemanager and collision detector and now I'd like to make an event system. i'm unsure how to go about this problem. Since it are user-defined events different parameters can be given to events. How can one accomplish this in c++?

Cheers xander
I thought about this once, and came to the conclusion that the only way to do this generically would be to create a "user event" type that stores generic data. I don't think you can get much better than that.
Damn that can't be true :(

Also, is there a way to implicitly convert a void* pointer to the argument of a function?

IE:
1
2
3
4
5
6
7
8
void print(int x)
{
    std::cout<<x;
}

int x = 0;
void* ptr = &x;
print(*ptr);
Last edited on
You don't necessarily have to make it a void*, I was thinking more along the lines of:

1
2
3
4
5
6
7
8
struct Userdata {
TypeEnum type;
union {
string name,
double number,
map<string,Userdata*> dict
};
};


I think this would look a lot better in a scripting language.

xander333 said "Damn that can't be true :("

Well, the entire Windows GUI OS messaging system runs like that.

Generic pointer types and a lot of casting depending on the event type.
Last edited on
Also, should the EventManager be a singleton?
Or how else am I going to make it available to everything?
The singleton pattern is kind of overused. Especially with events, you don't need global access to them - you can just propagate events through the interacting objects themselves.
I know the singleton pattern is misused very often.
So how should I make the EventManager visible to every object that needs to receive and send events?

calling an event:
1
2
3
4
5
6
EventManager mgr;
//sending events:
mgr.sendEvent(new MouseEvent(mousePos));

//registering for events:
mgr.hook("MouseEvent", &onClick);
Last edited on
My argument was actually that you don't need a manager at all.
How would you go about it then?
The purpose of the manager is to make sure my objects are loose-coupled...
I created an example using templates on how to create Events and use Event system in native C++

Please note that this is unfinished work, it's up to you to finish it as you want OK?!

This is the header file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#pragma once
#include <map>
#include <utility>

namespace events
{
	class HandlerBase
	{
	public:
		virtual void Process(short message) = 0;
	};


	template <typename TargetT>
	class Handler : public HandlerBase
	{
	private:
		typedef void (TargetT::*method_t)(short);
		TargetT* mObject;
		method_t mMethod;

	public:
		Handler(TargetT* object, method_t method)
			: mObject(object), mMethod(method) { }

		void Process(short param) override
		{
			(mObject->*mMethod)(param);
		}
	};


	class Event
	{
	private:
		typedef std::multimap<long, HandlerBase*> eventMap;

		long id;
		static long counter;
		static eventMap mapper;
		static eventMap::iterator iter;

		static eventMap StartMapping()
		{
			eventMap temp;
			return temp;
		}

	public:
		// TODO implement copy ctor and move assigment operator

		Event() : id(++counter) { };

		~Event()
		{
			for(iter = mapper.find(id); iter != mapper.end(); iter = mapper.find(id))
			{
				delete iter->second;
				mapper.erase(iter);
			}
		}

                // TODO implement mehod to remove a handler

		template<typename TargetT>
		void Register(TargetT* object, void (TargetT::*method)(short))
		{
			mapper.insert(std::make_pair(id, new Handler<TargetT>(object, method)));
		}

		void operator()(short param)
		{
			for(iter = mapper.begin(); iter != mapper.end(); ++iter)
				if (iter->first == id)
					iter->second->Process(param);
		}
	};
	long Event::counter = 0;
	Event::eventMap Event::mapper(Event::StartMapping());
	Event::eventMap::iterator Event::iter = Event::mapper.begin();


	class Object
	{
	protected:
		virtual ~Object() { }
		virtual void MessageLoop(short) = 0;

	public:
		template<typename TargetT>
		void Register(Event& evn, void (TargetT::*method)(short))
		{
			evn.Register(dynamic_cast<TargetT*>(this), method);
		}
	};
}


And this is a test of the above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include "Events.h"

using namespace std;
using namespace events;


class A : public Object
{
public: 
	Event MyEvent;
	void MessageLoop(short param) override
	{
		switch (param)
		{
		case 4:
			cout << "param 4 received" << endl;
			break;
		default:
			cout << "CLASS A param " << param << " received" << endl;
			break;
		}
	}
};

class B : public Object
{
public:
	Event TheEvent;
	void MessageLoop(short param) override
	{
		switch (param)
		{
		case 4:
			cout << "param 4 received" << endl;
			break;
		default:
			cout << "CLASS B param " << param << " received" << endl;
			break;
		}
	}
};

int main()
{
	A* zz = new A;
	B* xx = new B;

	xx->Register(zz->MyEvent, &B::MessageLoop);
	zz->Register(xx->TheEvent, &A::MessageLoop);

	zz->MyEvent(44);
	xx->TheEvent(100);

	delete xx;
	delete zz;
	cout << "\nDONE" << endl;
	cin.ignore();
	return 0;
}


So fat so good, it works like a charm but you may ask, and what if I want my mesage loop to of some other type and not void?
What if I wand custom handlers?

Here is an unfinished fully generic event system that works on any type any argument.

You can expand this to implement more arguments if you need but that's not necesay realy.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#pragma once
#include <map>
#include <utility>

namespace events
{
	template <typename ReturnT, typename ParamT>
	class HandlerBase
	{
	public:
		virtual ReturnT Process(ParamT message) = 0;
	};


	template <typename TargetT, typename ReturnT, typename ParamT>
	class Handler : public HandlerBase<ReturnT,ParamT>
	{
	private:
		typedef ReturnT (TargetT::*method_t)(ParamT);
		TargetT* mObject;
		method_t mMethod;

	public:
		Handler(TargetT* object, method_t method)
			: mObject(object), mMethod(method) { }

		ReturnT Process(ParamT param) override
		{
			return (mObject->*mMethod)(param);
		}
	};


	template <typename ReturnT,typename ParamT>
	class Event
	{
	private:
		typedef std::multimap<long, HandlerBase<ReturnT,ParamT>*> eventMap;

		long id;
		static long counter;
		static eventMap mapper;
		typename static eventMap::iterator iter;

		static eventMap StartMapping()
		{
			eventMap temp;
			return temp;
		}

	public:
		Event() : id(++counter) { };

		~Event()
		{
			for(iter = mapper.find(id); iter != mapper.end(); iter = mapper.find(id))
			{
				delete iter->second;
				mapper.erase(iter);
			}
		}

		template<typename TargetT>
		void attach(TargetT* object, ReturnT (TargetT::*method)(ParamT))
		{
			mapper.insert(std::make_pair(id, new Handler<TargetT, ReturnT, ParamT>(object, method)));
		}

		void Emit(ParamT param)
		{
			for(iter = mapper.begin(); iter != mapper.end(); ++iter)
				if (iter->first == id)
					iter->second->Process(param);
		}
	};
	template<typename ReturnT,typename ParamT> 
	long Event<typename ReturnT,typename ParamT>::counter = 0;

	template<typename ReturnT,typename ParamT>
	typename Event<typename ReturnT,typename ParamT>::eventMap::iterator
		Event<typename ReturnT,typename ParamT>::iter = Event<typename ReturnT,typename ParamT>::mapper.begin();

	template<typename ReturnT,typename ParamT> 
	typename Event<typename ReturnT,typename ParamT>::eventMap
		Event<typename ReturnT,typename ParamT>::mapper(Event<typename ReturnT,typename ParamT>::StartMapping());


	template<typename ReturnT,typename ParamT>
	class Object
	{
	protected:
		virtual ~Object() = 0 { }

	public:
		template<typename TargetT>
		void Register(Event<ReturnT, ParamT>& evn, ReturnT (TargetT::*method)(ParamT))
		{
			evn.attach(dynamic_cast<TargetT*>(this), method);
		}
	};
}

Topic archived. No new replies allowed.