1 |
/* |
2 |
* Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team |
3 |
* Rights to this code are as documented in doc/pod/gplicense.pod. |
4 |
* |
5 |
* A C++ callback system inspired by Boost.Signals. |
6 |
* |
7 |
* $Id: callback.h.in,v 1.2 2007-07-25 01:05:17 pippijn Exp $ |
8 |
*/ |
9 |
|
10 |
#ifndef CALLBACK_H |
11 |
#define CALLBACK_H |
12 |
|
13 |
#include <set> |
14 |
#include <list> |
15 |
|
16 |
namespace callback |
17 |
{ |
18 |
class event_base; |
19 |
|
20 |
class has_listeners |
21 |
{ |
22 |
private: |
23 |
typedef std::set <event_base *> sender_set; |
24 |
typedef sender_set::const_iterator const_iterator; |
25 |
typedef sender_set::iterator iterator; |
26 |
|
27 |
sender_set m_senders; |
28 |
sender_set &invokers (); |
29 |
|
30 |
const sender_set &invokers () const; |
31 |
|
32 |
public: |
33 |
has_listeners (); |
34 |
has_listeners (const has_listeners &rhs); |
35 |
|
36 |
virtual ~has_listeners (); |
37 |
|
38 |
has_listeners &operator = (const has_listeners &rhs); |
39 |
|
40 |
void add_invoker (event_base *sender); |
41 |
void remove_invoker (event_base *sender); |
42 |
|
43 |
protected: |
44 |
void copy_invokers (const has_listeners &rhs); |
45 |
void disconnect_all (); |
46 |
}; |
47 |
|
48 |
typedef has_listeners listening_base; |
49 |
|
50 |
|
51 |
class event_base |
52 |
{ |
53 |
public: |
54 |
virtual ~event_base () { } |
55 |
event_base () { } |
56 |
|
57 |
inline bool detach (listening_base *plistener) |
58 |
{ |
59 |
return this->listener_detach (plistener); |
60 |
} |
61 |
|
62 |
virtual bool listener_duplicate (const listening_base *poldlistener, listening_base *pnewlistener) = 0; |
63 |
|
64 |
protected: |
65 |
virtual bool listener_detach (listening_base *plistener) = 0; |
66 |
|
67 |
template <typename list_T> |
68 |
void free_connections (list_T &list) |
69 |
{ |
70 |
typename list_T::const_iterator it; |
71 |
typename list_T::const_iterator it_end = list.end (); |
72 |
|
73 |
for (it = list.begin (); it != it_end; ++it) |
74 |
{ |
75 |
(*it)->getdest ()->remove_invoker (this); |
76 |
delete *it; |
77 |
} |
78 |
|
79 |
list.clear (); |
80 |
} |
81 |
|
82 |
template <typename list_T> |
83 |
bool detach (listening_base *plistener, list_T &list) |
84 |
{ |
85 |
typename list_T::iterator it; |
86 |
typename list_T::iterator it_end = list.end (); |
87 |
|
88 |
for (it = list.begin (); it != it_end; ++it) |
89 |
{ |
90 |
if ((*it)->getdest () == plistener) |
91 |
{ |
92 |
delete *it; |
93 |
list.erase (it); |
94 |
plistener->remove_invoker (this); |
95 |
|
96 |
return true; |
97 |
} |
98 |
} |
99 |
|
100 |
return false; |
101 |
} |
102 |
|
103 |
template <typename listeningT, typename SetT> |
104 |
bool duplicate_connection (const listeningT *oldtarget, listeningT *newtarget, SetT &list) |
105 |
{ |
106 |
typename SetT::iterator it = list.begin (); |
107 |
typename SetT::iterator it_end = list.end (); |
108 |
|
109 |
bool ret = false; |
110 |
for (; it != it_end; ++it) |
111 |
{ |
112 |
if ((*it)->getdest () == oldtarget) |
113 |
{ |
114 |
list.push_back ((*it)->duplicate (newtarget)); |
115 |
ret = true; |
116 |
} |
117 |
} |
118 |
return ret; |
119 |
} |
120 |
|
121 |
template <typename SetT> |
122 |
void copy_connections (const SetT &src, SetT &tgt) |
123 |
{ |
124 |
if (&src == &tgt) |
125 |
return ; |
126 |
typename SetT::const_iterator it = src.begin (); |
127 |
typename SetT::const_iterator it_end = src.end (); |
128 |
typedef typename SetT::value_type ConT; |
129 |
ConT clone = 0; |
130 |
for (; it != it_end; ++it) |
131 |
{ |
132 |
clone = (*it)->clone (); |
133 |
if (!clone) |
134 |
continue; |
135 |
clone->getdest ()->add_invoker (this); |
136 |
tgt.push_back (clone); |
137 |
} |
138 |
} |
139 |
}; |
140 |
|
141 |
class connection_base0 |
142 |
{ |
143 |
public: |
144 |
virtual ~connection_base0 () { } |
145 |
typedef connection_base0 this_type; |
146 |
virtual listening_base *getdest () const = 0; |
147 |
virtual void invoke () const = 0; |
148 |
|
149 |
virtual this_type *clone () const = 0; |
150 |
virtual this_type *duplicate (listening_base *pnewdest) const = 0; |
151 |
}; |
152 |
|
153 |
|
154 |
template <class arg1_type> |
155 |
class connection_base1 |
156 |
{ |
157 |
public: |
158 |
virtual ~connection_base1 () { } |
159 |
typedef connection_base1 <arg1_type> this_type; |
160 |
virtual listening_base *getdest () const = 0; |
161 |
virtual void invoke (arg1_type) const = 0; |
162 |
|
163 |
virtual this_type *clone () const = 0; |
164 |
virtual this_type *duplicate (listening_base *pnewdest) const = 0; |
165 |
}; |
166 |
|
167 |
|
168 |
template <class dest_type> |
169 |
class connection0 : public connection_base0 |
170 |
{ |
171 |
public: |
172 |
typedef connection_base0 base_type; |
173 |
typedef connection0<dest_type> this_type; |
174 |
|
175 |
typedef void (dest_type::*listener_fun) (); |
176 |
connection0 (): m_pobject (0), m_pmemfun (0){} |
177 |
|
178 |
virtual ~connection0 () { } |
179 |
|
180 |
connection0 (dest_type *pobject, listener_fun pmemfun): m_pobject (pobject), m_pmemfun (pmemfun){} |
181 |
|
182 |
virtual base_type *clone () const |
183 |
{ |
184 |
return new this_type (*this); |
185 |
} |
186 |
|
187 |
virtual base_type *duplicate (listening_base *pnewdest) const |
188 |
{ |
189 |
return new this_type (dynamic_cast <dest_type *> (pnewdest), m_pmemfun); |
190 |
} |
191 |
|
192 |
virtual void invoke () const |
193 |
{ |
194 |
(m_pobject->*m_pmemfun) (); |
195 |
} |
196 |
|
197 |
virtual listening_base *getdest () const |
198 |
{ |
199 |
return m_pobject; |
200 |
} |
201 |
|
202 |
private: |
203 |
dest_type *m_pobject; |
204 |
listener_fun m_pmemfun; |
205 |
}; |
206 |
|
207 |
template <class dest_type, class arg1_type> |
208 |
class connection1 : public connection_base1<arg1_type> |
209 |
{ |
210 |
public: |
211 |
typedef connection_base1<arg1_type> base_type; |
212 |
typedef connection1<dest_type, arg1_type> this_type; |
213 |
typedef void (dest_type::*listener_fun) (arg1_type); |
214 |
connection1 (): m_pobject (0), m_pmemfun (0){} |
215 |
|
216 |
connection1 (dest_type *pobject, listener_fun pmemfun): m_pobject (pobject), m_pmemfun (pmemfun){} |
217 |
|
218 |
virtual ~connection1 () { } |
219 |
|
220 |
virtual base_type *clone () const |
221 |
{ |
222 |
return new this_type (*this); |
223 |
} |
224 |
|
225 |
virtual base_type *duplicate (listening_base *pnewdest) const |
226 |
{ |
227 |
return new this_type (dynamic_cast <dest_type *> (pnewdest), m_pmemfun); |
228 |
} |
229 |
|
230 |
virtual void invoke (arg1_type a1) const |
231 |
{ |
232 |
(m_pobject->*m_pmemfun) (a1); |
233 |
} |
234 |
|
235 |
virtual listening_base *getdest () const |
236 |
{ |
237 |
return m_pobject; |
238 |
} |
239 |
|
240 |
private: |
241 |
dest_type *m_pobject; |
242 |
listener_fun m_pmemfun; |
243 |
}; |
244 |
|
245 |
|
246 |
class event0 : public event_base |
247 |
{ |
248 |
public: |
249 |
typedef std::list <connection_base0 *> connection_list; |
250 |
|
251 |
event0 () { } |
252 |
|
253 |
event0 (const event0 &s): event_base (s) |
254 |
{ |
255 |
this->copy_connections (s.connections (), this->connections ()); |
256 |
} |
257 |
|
258 |
event0 &operator = (const event0 &s) |
259 |
{ |
260 |
this->copy_connections (s.connections (), this->connections ()); |
261 |
return *this; |
262 |
} |
263 |
|
264 |
virtual ~event0 () |
265 |
{ |
266 |
this->disconnect_all (); |
267 |
} |
268 |
|
269 |
void disconnect_all () |
270 |
{ |
271 |
this->free_connections (this->connections ()); |
272 |
} |
273 |
|
274 |
|
275 |
template <class desttype> |
276 |
void attach (desttype *pclass, void (desttype::*pmemfun) ()) |
277 |
{ |
278 |
connection_base0 *conn = new connection0<desttype> (pclass, pmemfun); |
279 |
this->connections ().push_back (conn); |
280 |
pclass->add_invoker (this); |
281 |
} |
282 |
|
283 |
|
284 |
bool listener_duplicate (const listening_base *oldtarget, listening_base *newtarget) |
285 |
{ |
286 |
return this->duplicate_connection (oldtarget, newtarget, this->connections ()); |
287 |
} |
288 |
|
289 |
|
290 |
void invoke () const |
291 |
{ |
292 |
connection_list::const_iterator it; |
293 |
connection_list::const_iterator end = this->connections ().end (); |
294 |
|
295 |
for (it = this->connections ().begin (); it != end; ++it) |
296 |
(*it)->invoke (); |
297 |
} |
298 |
|
299 |
void operator () () const |
300 |
{ |
301 |
this->invoke (); |
302 |
} |
303 |
|
304 |
protected: |
305 |
connection_list &connections () |
306 |
{ |
307 |
return this->m_connected_listeners; |
308 |
} |
309 |
|
310 |
const connection_list &connections () const |
311 |
{ |
312 |
return this->m_connected_listeners; |
313 |
} |
314 |
|
315 |
bool listener_detach (listening_base *plistener) |
316 |
{ |
317 |
return this->detach (plistener, this->connections ()); |
318 |
} |
319 |
|
320 |
private: |
321 |
connection_list m_connected_listeners; |
322 |
}; |
323 |
|
324 |
template <class arg1_type> |
325 |
class event1 : public event_base |
326 |
{ |
327 |
public: |
328 |
typedef std::list<connection_base1<arg1_type> *> connection_list; |
329 |
|
330 |
event1 () { } |
331 |
|
332 |
event1 (const event1 <arg1_type> &s): event_base (s) { } |
333 |
|
334 |
template <class dest_type> |
335 |
void attach (dest_type *pclass, void (dest_type::*pmemfun) (arg1_type)) |
336 |
{ |
337 |
connection_base1<arg1_type> *conn = new connection1<dest_type, arg1_type> (pclass, pmemfun); |
338 |
this->connections ().push_back (conn); |
339 |
pclass->add_invoker (this); |
340 |
} |
341 |
|
342 |
void invoke (arg1_type a1) const |
343 |
{ |
344 |
typename connection_list::const_iterator it; |
345 |
typename connection_list::const_iterator it_end = this->connections ().end (); |
346 |
|
347 |
for (it = this->connections ().begin (); it != it_end; ++it) |
348 |
{ |
349 |
(*it)->invoke (a1); |
350 |
} |
351 |
} |
352 |
|
353 |
void operator () (arg1_type a1) const |
354 |
{ |
355 |
this->invoke (a1); |
356 |
} |
357 |
|
358 |
event1 &operator = (const event1 &s) |
359 |
{ |
360 |
this->copy_connections (s.connections (), this->connections ()); |
361 |
return *this; |
362 |
} |
363 |
|
364 |
bool listener_duplicate (const listening_base *oldtarget, listening_base *newtarget) |
365 |
{ |
366 |
return this->duplicate_connection (oldtarget, newtarget, this->connections ()); |
367 |
} |
368 |
|
369 |
~event1 () |
370 |
{ |
371 |
this->disconnect_all (); |
372 |
} |
373 |
|
374 |
void disconnect_all () |
375 |
{ |
376 |
this->free_connections (connections ()); |
377 |
} |
378 |
|
379 |
protected: |
380 |
connection_list &connections () |
381 |
{ |
382 |
return this->m_connected_listeners; |
383 |
} |
384 |
|
385 |
const connection_list &connections () const |
386 |
{ |
387 |
return this->m_connected_listeners; |
388 |
} |
389 |
|
390 |
bool listener_detach (listening_base *plistener) |
391 |
{ |
392 |
return this->detach (plistener, this->connections ()); |
393 |
} |
394 |
|
395 |
private: |
396 |
connection_list m_connected_listeners; |
397 |
}; |
398 |
|
399 |
@CALLBACKS@ |
400 |
|
401 |
} // namespace callback |
402 |
|
403 |
class has_callbacks |
404 |
{ |
405 |
protected: |
406 |
struct callbacks |
407 |
{ |
408 |
callback::event1 <has_callbacks *> created; |
409 |
callback::event1 <has_callbacks *> destroyed; |
410 |
}; |
411 |
|
412 |
static callbacks callback; |
413 |
|
414 |
has_callbacks (); |
415 |
~has_callbacks (); |
416 |
}; |
417 |
|
418 |
#endif // CALLBACK_H |