Mercurial > projects > ldc
comparison tango/tango/core/Signal.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /** | |
2 * The signal module provides a basic implementation of the listener pattern | |
3 * using the "Signals and Slots" model from Qt. | |
4 * | |
5 * Copyright: Copyright (C) 2005-2006 Sean Kelly. All rights reserved. | |
6 * License: BSD style: $(LICENSE) | |
7 * Authors: Sean Kelly | |
8 */ | |
9 module tango.core.Signal; | |
10 | |
11 | |
12 private import tango.core.Array; | |
13 | |
14 | |
15 /** | |
16 * A signal is an event which contains a collection of listeners (called | |
17 * slots). When a signal is called, that call will be propagated to each | |
18 * attached slot in a synchronous manner. It is legal for a slot to call a | |
19 * signal's attach and detach methods when it is signaled. When this occurs, | |
20 * attach events will be queued and processed after the signal has propagated | |
21 * to all slots, but detach events are processed immediately. This ensures | |
22 * that it is safe for slots to be deleted at any time, even within a slot | |
23 * routine. | |
24 * | |
25 * Example: | |
26 * ----------------------------------------------------------------------------- | |
27 * | |
28 * class Button | |
29 * { | |
30 * Signal!(Button) press; | |
31 * } | |
32 * | |
33 * void wasPressed( Button b ) | |
34 * { | |
35 * printf( "Button was pressed.\n" ); | |
36 * } | |
37 * | |
38 * Button b = new Button; | |
39 * | |
40 * b.press.attach( &wasPressed ); | |
41 * b.press( b ); | |
42 * | |
43 * ----------------------------------------------------------------------------- | |
44 * | |
45 * Please note that this implementation does not use weak pointers to store | |
46 * references to slots. This design was chosen because weak pointers are | |
47 * inherently unsafe when combined with non-deterministic destruction, with | |
48 * many of the same limitations as destructors in the same situation. It is | |
49 * still possible to obtain weak-pointer behavior, but this must be done | |
50 * through a proxy object instead. | |
51 */ | |
52 struct Signal( Args... ) | |
53 { | |
54 alias void delegate(Args) SlotDg; /// | |
55 alias void function(Args) SlotFn; /// | |
56 | |
57 alias opCall call; /// Alias to simplify chained calling. | |
58 | |
59 | |
60 /** | |
61 * The signal procedure. When called, each of the attached slots will be | |
62 * called synchronously. | |
63 * | |
64 * args = The signal arguments. | |
65 */ | |
66 void opCall( Args args ) | |
67 { | |
68 synchronized | |
69 { | |
70 m_blk = true; | |
71 | |
72 for( size_t i = 0; i < m_dgs.length; ++i ) | |
73 { | |
74 if( m_dgs[i] !is null ) | |
75 m_dgs[i]( args ); | |
76 } | |
77 m_dgs.length = m_dgs.remove( cast(SlotDg) null ); | |
78 | |
79 for( size_t i = 0; i < m_fns.length; ++i ) | |
80 { | |
81 if( m_fns[i] !is null ) | |
82 m_fns[i]( args ); | |
83 } | |
84 m_fns.length = m_fns.remove( cast(SlotFn) null ); | |
85 | |
86 m_blk = false; | |
87 | |
88 procAdds(); | |
89 } | |
90 } | |
91 | |
92 | |
93 /** | |
94 * Attaches a delegate to this signal. A delegate may be either attached | |
95 * or detached, so successive calls to attach for the same delegate will | |
96 * have no effect. | |
97 * | |
98 * dg = The delegate to attach. | |
99 */ | |
100 void attach( SlotDg dg ) | |
101 { | |
102 synchronized | |
103 { | |
104 if( m_blk ) | |
105 { | |
106 m_add ~= Add( dg ); | |
107 } | |
108 else | |
109 { | |
110 auto pos = m_dgs.find( dg ); | |
111 if( pos == m_dgs.length ) | |
112 m_dgs ~= dg; | |
113 } | |
114 } | |
115 } | |
116 | |
117 | |
118 /** | |
119 * Attaches a function to this signal. A function may be either attached | |
120 * or detached, so successive calls to attach for the same function will | |
121 * have no effect. | |
122 * | |
123 * fn = The function to attach. | |
124 */ | |
125 void attach( SlotFn fn ) | |
126 { | |
127 synchronized | |
128 { | |
129 if( m_blk ) | |
130 { | |
131 m_add ~= Add( fn ); | |
132 } | |
133 else | |
134 { | |
135 auto pos = m_fns.find( fn ); | |
136 if( pos == m_fns.length ) | |
137 m_fns ~= fn; | |
138 } | |
139 } | |
140 } | |
141 | |
142 | |
143 /** | |
144 * Detaches a delegate from this signal. | |
145 * | |
146 * dg = The delegate to detach. | |
147 */ | |
148 void detach( SlotDg dg ) | |
149 { | |
150 synchronized | |
151 { | |
152 auto pos = m_dgs.find( dg ); | |
153 if( pos < m_dgs.length ) | |
154 m_dgs[pos] = null; | |
155 } | |
156 } | |
157 | |
158 | |
159 /** | |
160 * Detaches a function from this signal. | |
161 * | |
162 * fn = The function to detach. | |
163 */ | |
164 void detach( SlotFn fn ) | |
165 { | |
166 synchronized | |
167 { | |
168 auto pos = m_fns.find( fn ); | |
169 if( pos < m_fns.length ) | |
170 m_fns[pos] = null; | |
171 } | |
172 } | |
173 | |
174 | |
175 private: | |
176 struct Add | |
177 { | |
178 enum Type | |
179 { | |
180 DG, | |
181 FN | |
182 } | |
183 | |
184 static Add opCall( SlotDg d ) | |
185 { | |
186 Add e; | |
187 e.ty = Type.DG; | |
188 e.dg = d; | |
189 return e; | |
190 } | |
191 | |
192 static Add opCall( SlotFn f ) | |
193 { | |
194 Add e; | |
195 e.ty = Type.FN; | |
196 e.fn = f; | |
197 return e; | |
198 } | |
199 | |
200 union | |
201 { | |
202 SlotDg dg; | |
203 SlotFn fn; | |
204 } | |
205 Type ty; | |
206 } | |
207 | |
208 | |
209 void procAdds() | |
210 { | |
211 foreach( a; m_add ) | |
212 { | |
213 if( a.ty == Add.Type.DG ) | |
214 m_dgs ~= a.dg; | |
215 else | |
216 m_fns ~= a.fn; | |
217 } | |
218 m_add.length = 0; | |
219 } | |
220 | |
221 | |
222 SlotDg[] m_dgs; | |
223 SlotFn[] m_fns; | |
224 Add[] m_add; | |
225 bool m_blk; | |
226 } | |
227 | |
228 | |
229 debug( UnitTest ) | |
230 { | |
231 unittest | |
232 { | |
233 class Button | |
234 { | |
235 Signal!(Button) press; | |
236 } | |
237 | |
238 int count = 0; | |
239 | |
240 void wasPressedA( Button b ) | |
241 { | |
242 ++count; | |
243 } | |
244 | |
245 void wasPressedB( Button b ) | |
246 { | |
247 ++count; | |
248 } | |
249 | |
250 Button b = new Button; | |
251 | |
252 b.press.attach( &wasPressedA ); | |
253 b.press( b ); | |
254 assert( count == 1 ); | |
255 | |
256 count = 0; | |
257 b.press.attach( &wasPressedB ); | |
258 b.press( b ); | |
259 assert( count == 2 ); | |
260 | |
261 count = 0; | |
262 b.press.attach( &wasPressedA ); | |
263 b.press( b ); | |
264 assert( count == 2 ); | |
265 | |
266 count = 0; | |
267 b.press.detach( &wasPressedB ); | |
268 b.press( b ); | |
269 assert( count == 1 ); | |
270 | |
271 count = 0; | |
272 b.press.detach( &wasPressedA ); | |
273 b.press( b ); | |
274 assert( count == 0 ); | |
275 } | |
276 } |