is it possible to write a container for different boost::signal

Hi,
I'm trying to write a UI lib and got a problem when implementing the Observer model.
I've read the tutorial about boost::signal, it's beautiful, but I need to do some wrap for it.
for example, a Window may have some different events, like "move","show",etc. Parameters for these events are different:
1
2
"move" => signal<void(int, int)>     // "move" needs two int params: xpos, ypos
"show" => signal<void(bool)>         // "show" needs one bool param: show/hide 

the implementation of Window will be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Window {
private:
	signal<void(int,int)> moveSignal;
	signal<void(bool)> showSignal;

	void onMove(int x, int y) {
		// do some thing
		moveSignal(x, y);
	}
	void onShow(bool show) {
		// do some thing
		showSignal(show);
	}
}


if there are lots of events, there'll be lots of xxSignal defined in class Window, I think it should be abstract to a class Observer:
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
class Observer {
private:
	map<string, signal.....> signalContainer;// how to write the container?

public:

	template<typename T>
	void addListener(string eventName, T fn) {
		signalContainer[eventName]->connect(fn);
	}
	template<typename T1, typename T2>
	void addListener(string eventName, T1 object, T2 fn) {
		signalContainer[eventName]->connect(obj, fn);
	}


	void fireEvent(string eventName) {
		signalContainer[eventName]();
	}
	template<typename T1>
	void fireEvent(string eventName, T1 arg1) {
		signalContainer[eventName](arg1);
	}
	template<typename T1, typename T2>
	void fireEvent(string eventName, T arg1, T arg2) {
		signalContainer[eventName](arg1, arg2);
	}
};


// usage
class Window : public Observer {
	// ...
public:
	Window() {
		// ...
		this.addListener("move", this, Window::onMove);
	}
	void onMove(int x, int y) {
		cout << "move to:" << x << ", " << y << endl;
	}
};

int main() {
	Window* w = new Window();
	w.fireEvent("move", 200, 300);

	return 0;
}


stl::map can't hold different signals, is it possible to write such a wrap for the different signals?

Thanks.
Last edited on
You cannot do this. And I don't see what the observer class buys you.

Wouldn't you just want to call w->move(200, 300)? The name/signal lookup is going to kill performance.



Hi PanGalactic, Thank you for the patient.

the test case isn't that good, actually the
w.fireEvent("move", 200, 300);
is called when move the window by mouse drag.

with out the Observer, all the events(signals) are defined in the class Window, if Window has 15 events, the class would be look like:
1
2
3
4
5
6
class Window {
private:
	signal<void(int,int)> moveSignal;
	signal<void(bool)> showSignal;
....12 signals
	signal<void(......


it looks ugly, they could be removed as long as Window extends Observer.

I'm not good at C++, how do those famous UI library implement the Observer?

Thanks.

it looks ugly


Really?

I think w.fireEvent("move", 200, 300); looks ugly. As PanGalactic, I don't see any reason for this yet-another-indirection. It doesn't buy you anything except more complex code and long and ugly "addListener" - chains.


Said that, there may be reasons to be able to iterate over "all events this class could raise", for example when implementing an UI-editor which want to iterate over other objects (not known to the binary) and display event information of these objects.

So if you really want to stick to your path, you should change your signals to a uniform signature, e.g. by passing a pointer/reference to some "class BaseEvent" - base class, which provides reflection capabilities.

But prepare for the worst - that's gonna look really ugly in the end. ;)

Actually, what the OP wants may be useful. I have no experience to say, but... You don't need to know the types of events that the object actually supports in order to subscribe for them. It allows you to not know the concrete type of the event producer and use sort of duck-typing. Also, you may iterate over the subscribers and search for specific type of events, when there are too many variations to declare all of them in advance.

On the other hand, as the other posters said, declaring the events that you support is self documentation. It is actually desirable. Unless you support gazillions for some reason.

Just by reading briefly, I suspect you may use function objects as wrappers with vararg call operator that extracts boost::variant arguments. Not very compile time type safe, but may do the job.

Regards
Topic archived. No new replies allowed.