implement observer pattern

Hi,
I'm implementing an Observable class, any class would gain the ability of the observer as long as it derives from Observable. it has two method
on -> register events
fire -> fires events, all registered handler will be called

here's what I do:
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
// delegate.h
#include <windows.h>

#include <functional>
using namespace std;

template<typename T>
struct delegate;

template<>
struct delegate<void()> {
	typedef function<void()> fn_type;
	fn_type fn;
	delegate(fn_type _fn) : fn(_fn) {}
	void operator()() { fn(); }
};
template<typename T1>
struct delegate<void(T1)> {
	typedef function<void(T1)> fn_type;
	fn_type fn;
	delegate(fn_type _fn) : fn(_fn) {}
	void operator()(T1 a1) { fn(a1); }
};
template<typename T1, typename T2>
struct delegate<void(T1,T2)> {
	typedef function<void(T1,T2)> fn_type;
	fn_type fn;
	delegate(fn_type _fn) : fn(_fn) {}
	void operator()(T1 a1, T2 a2) { fn(a1, a2); }
};
// ... and structs for 3+ arguments 


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
// Observable.h
#include <windows.h>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
#include "delegate.h"

#include <iostream>

#define EVENT string

struct Observable {
	Observable() {}
	~Observable() {
		// how to clean up
	}

	map<EVENT, vector<void*>> _obs;

	template<typename T>
	void on(EVENT event, T* dele) {
		_obs[event].push_back((void*)dele);
	}

	void fire(EVENT event) { // no argument
		vector<void*>& v = _obs[event];
		for(unsigned int i=0; i<v.size(); i++) {
			((delegate<void()>*)v[i])->operator()();
		}
	}
	template<typename T1>
	void fire(EVENT event, T1 a1) { // 1 argument
		vector<void*>& v = _obs[event];
		for(unsigned int i=0; i<v.size(); i++) {
			((delegate<void(T1)>*)v[i])->operator()(a1);
		}
	}
	template<typename T1, typename T2>
	void fire(EVENT event, T1 a1, T2 a2) { // 2 arguments
		vector<void*>& v = _obs[event];
		for(unsigned int i=0; i<v.size(); i++) {
			((delegate<void(T1,T2)>*)v[i])->operator()(a1,a2);
		}
	}
	// ...functions for 3+ paramaters
};


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
#include <windows.h>
#include "../include/observable.h"
#include <string>
#include <iostream>
using namespace std;

#define EVT0 "0"
#define EVT1 "1"
#define EVT2 "2"
#define EVT3 "3"
#define EVT4 "4"
#define EVT5 "5"

int main() {

	Observable ob;

	ob.on(EVT0, new delegate<void()>([&]() {
		printf("%d, %d\n", 0, 0);
	}));
	ob.on(EVT1, new delegate<void(int)>([&](int a) {
		printf("%d====\n",  a);
	}));
	ob.on(EVT2, new delegate<void(int,int)>([&](int a, int b) {
		printf("%d, %d\n", a, b);
	}));
	ob.on(EVT3, new delegate<void(string,string,int)>([&](string a, string b, int c) {
		cout << a << "  " << b << "  " << c << endl;
	}));
	ob.on(EVT4, new delegate<void(string,string,string,int)>([&](string a, string b, string c, int d) {
		cout << a << "  " << b << "  " << c << "  " << d << endl;
	}));
	ob.on(EVT5, new delegate<void(string,string,string,int, int)>([&](string a, string b, string c, int d, int e) {
		cout << a << "  " << b << "  " << c << "  " << d << "  " << e << endl;
	}));

	ob.fire(EVT0);
	ob.fire(EVT1, 1111);
	ob.fire(EVT2, 222, 2222);
	ob.fire(EVT3, string("3333"),string("3333"), 3);
	ob.fire(EVT4, string("44"), string("4"), string("4444"), 44);
	ob.fire(EVT5, string("555"), string("555"), string("55"), 5, 5);
}


Now I have two problems
1. how to release the resource when destroying the Observable
2. the way of registering event is urgly, it looks like:
ob.on(EVT3, new delegate<void(string,string,int)>([&](string a, string b, int c) {}));
anyway to improve this?

Many Thanks.
First off, shouldn't Observable be called Observer or Notifiable? It's not like these objects are observed by another object. They observe.

It seems like you've coded yourself into a corner. Maybe try something like this instead?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct base_delegate{
    base_delegate(){}
    virtual ~base_delegate(){}
    virtual void operator()()=0;
};

template<typename T>
struct delegate:public base_delegate;

//...
struct Observable {
//...
    map<EVENT, vector<base_delegate *>> _obs;
//... 
yeah Thanks helios.
with delegate_base the first problem solved
Your Observable class, this is the "subject"? An observer is a "handler"/"function" passed to the .on method? Will observer be able to detach at anytime? Is this meant for singly or multithreaded?

anyway to improve this?
You could use a factory to create your handlers?
Last edited on
Thanks for reading.
Your Observable class, this is the "subject"? An observer is a "handler"/"function"...

i'm trying to combine the "subject"/"observer" into one class, it has both .on() and .fire() method, any class derives from it could have these ability

i have not yet thought about detach and multithreaded, maybe .on() could return an ID, like
1
2
3
4
5
6
7
template<typename T>
int on(EVENT event, T* dele) {
	_obs[event].push_back((void*)dele);
	return _obs[event].size();
}
int detach_id = win.on("resize", ...);
//use the detach_id and event name to delete the event handler 


and how to use a factory to create the handlers..
detach and multithreaded

If you are in the middle of notifying, and an observer removes itself you could have a problem, same thing with attaching, neither are thread safe.

I recently implemented an observer pattern. I did some things differently, which worked for me. Here are some of the things I did.

For subscribers, the interface I exposed to them for attaching was.
1) An event they were interested in (or multiple events).
From an interface standpoint it was the same, how did I do this, I created an event parameter class that used named parameter idiom to select 1 or more or even all events for a particular observable.
2) A handle to the subscriber.


So, code example, for attaching, instead of multiple threads the signature for my .on looked like this:

void attach(IObserver *, IEvents *);

After re-reading what I wrote that does not apply to your multiple params. But what does simply that part for me, the following code does apply.

1
2
3
struct IObserver{
   void Notify(ISubject*)=0;
}


Where ISubject can act as zero params or 8 params, it's implementation defined.


Multithreaded issues and solution (there is way more ways to solve, the one I chose worked for me). I created a thread who's sole job was to consume attach/detach requests from subscribers. I used a condition variable and a critical section. When a subscriber attached, if the observable it was trying to attach to was not in the process of notifying it would immediately add to my subscriber list (or remove). If it was in the process of notifying then the request would be pushed into a queue, once the notification was done the condition variable would be triggered signalling the request thread to apply the attach/detach of the subscriber. I had to do this because in my cause when observables were notifying I was not allowed to hold a lock on the notification, this was just a requirement of my system.
Last edited on
Thanks clanmjc,
multiple param is the tricky part, I'll look into that tomorrow, it's 2 o'clock in the morning now.
Here are my observations of what you've posted, which may be incorrect.
1) When you attach an event, known at time of attachment you also register X param function.
1a) From this observation, it looks like the event type directly correlates to the number of params of your handler (maybe this is incorrect but looks like it from your example, did you mean for this or it is just the example?).

How can this be simplified?
1) Attach only an event, register a handler that has a pointer/reference to some arbitrary implementation of what a param for that event type would be.

Example: (Let ISubject take the place of multiple params)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ISubject{
   virtual ~ISubject(){}
    
};


//two param
struct TwoParamImpl : public ISubject{
   //implementation
};

//Very complex param with multiple params and complex types
struct RocketScientestParamImpl : public ISubject{
   //impl...
};


Now when you fire off notifications
1
2
3
4
5
6
7
8
9
//Void notify
ob.fire(EVT0);

//Same event but now with 2 param
TwoParamImpl subject;  //Assume setting/initializing etc to the object
ob.fire(EVT0, &subject);

RocketScientestParamImpl rktSienceSubject;
ob.fire(EVT4, &rktSienceSubject);


Where fire looks something like: (I didn't use event, nor did I handle multithreads in this example)
1
2
3
4
5
void fire(EVENT event, ISubject * pSub) { 
		Observers = _obs[event];
                 for_each(Observer in Observers)
                     Observer->Notify(pSub);
	}
Last edited on
it looks traditional, with ISubject introduced, each event needs an extra struct, it should be inconvenient.
I googled a lot and find the variadic template, which is a really cool feature in c++0x, now the code looks like:

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
//delegate.h
#pragma once
#include <windows.h>

#include <functional>
using namespace std;

struct delegate_base {
	delegate_base() {}
	virtual ~delegate_base() {}
};
template<typename T>
struct delegate;

template<typename... Args>
struct delegate<void(Args...)> : public delegate_base {
	typedef function<void(Args...)> fn_type;
	fn_type fn;
	delegate(fn_type _fn) : fn(_fn) {}
	virtual void operator()(Args... args) { fn(args...); }
};

// template<typename... Args>
// delegate_base* make_delegate(function<void(Args...)> fn) {
// 	return new delegate<void(Args...)>(fn);
// } 

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
// observer.h
#pragma once

#include <windows.h>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
#include "delegate.h"

#define EVENT string

struct Observer {
	Observer() {}
	~Observer() {
		for_each(_obs.begin(), _obs.end(), [&](pair<EVENT, vector<delegate_base*>> p) {
			for_each(p.second.begin(), p.second.end(), [&](delegate_base* d) {
				delete d;
			});
		});
	}
	typedef map<EVENT, vector<delegate_base*>> ob_t;
	ob_t _obs;


	template<typename T>
	void on(EVENT event, T* dele) {
		_obs[event].push_back((delegate_base*)dele);
	}

	template<class... Args>
	void fire(EVENT event, Args... args) { // no argument
		vector<delegate_base*>& v = _obs[event];
		for(unsigned int i=0; i<v.size(); i++) {
			((delegate<void(Args...)>*)v[i])->operator()(args...);
		}
	}
};

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
//test.cpp
#include <windows.h>
#include "../include/observer.h"
#include <string>
#include <iostream>
using namespace std;

class Window : public Observer {
};
int main() {

	Window win;

	win.on("activate", new delegate<void()>([&]() {
		printf("window is activated\n");
	}));
	win.on("title_change", new delegate<void(string)>([&](string title) {
		printf("title changed to: %s\n",  title.c_str());
	}));
	win.on("move", new delegate<void(int,int)>([&](int x, int y) {
		printf("window moved to (%d, %d)\n", x, y);
	}));

	win.fire("activate");
	win.fire("title_change", string("New Title"));
	win.fire("move", 400, 300);
}


but the way of registering event is still urgly.
Topic archived. No new replies allowed.