Mercurial > projects > qtd
annotate qt/d1/qt/Signal.d @ 280:bcc498ccf334
Fix for windows build
author | SokoL_SD |
---|---|
date | Fri, 09 Oct 2009 08:43:15 +0000 |
parents | 519befd5a5d1 |
children | 256ab6cb8e85 |
rev | line source |
---|---|
1 | 1 /** |
2 * | |
3 * Copyright: Copyright QtD Team, 2008-2009 | |
4 * Authors: Max Samukha, Eldar Insafutdinov | |
5 * License: <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a> | |
6 * | |
7 * Copyright QtD Team, 2008-2009 | |
8 * Distributed under the Boost Software License, Version 1.0. | |
9 * (See accompanying file boost-license-1.0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
10 * | |
11 */ | |
16 | 12 module qt.Signal; |
1 | 13 |
14 public import qt.QGlobal; | |
15 import tango.core.Exception; | |
16 import tango.core.Traits; | |
17 import tango.core.Thread; | |
278 | 18 public import tango.core.Tuple; |
1 | 19 import tango.stdc.stdlib : crealloc = realloc, cfree = free; |
20 import tango.stdc.string : memmove; | |
21 | |
22 private: // private by default | |
23 | |
24 alias void delegate(Object) DEvent; | |
25 | |
26 extern(C) void rt_attachDisposeEvent(Object o, DEvent e); | |
27 extern(C) void rt_detachDisposeEvent(Object o, DEvent e); | |
28 extern(C) Object _d_toObject(void* p); | |
29 | |
30 void realloc(T)(ref T[] a, size_t length) | |
31 { | |
32 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; | |
33 if (!a.ptr) | |
34 new OutOfMemoryException(__FILE__, __LINE__); | |
35 } | |
36 | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
37 void append(T)(ref T[] a, T element) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
38 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
39 auto newLen = a.length + 1; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
40 a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
41 if (!a.ptr) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
42 new OutOfMemoryException(__FILE__, __LINE__); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
43 a[newLen - 1] = element; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
44 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
45 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
46 void move(T)(ref T[] a, size_t src, size_t dest, size_t length) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
47 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
48 if (a.length > 1) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
49 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
50 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
51 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
52 // COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
53 // with Array.remove |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
54 void erase(T)(ref T[] a, size_t i) |
1 | 55 { |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
56 auto newLen = a.length - 1; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
57 move(a, i + 1, i, newLen); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
58 realloc(a, newLen); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
59 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
60 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
61 version (QtdUnittest) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
62 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
63 unittest |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
64 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
65 int[] a; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
66 realloc(a, 16); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
67 assert(a.length == 16); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
68 foreach (i, ref e; a) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
69 e = i; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
70 realloc(a, 4096); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
71 assert(a.length == 4096); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
72 foreach (i, e; a[0..16]) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
73 assert(e == i); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
74 cfree(a.ptr); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
75 } |
1 | 76 } |
77 | |
78 // TODO: This one should be replaced with an appropriate library function | |
79 char[] __toString(long v) | |
80 { | |
81 if (v == 0) | |
82 return "0"; | |
83 | |
84 char[] ret; | |
85 | |
86 bool neg; | |
87 if (v < 0) | |
88 { | |
89 neg = true; | |
90 v = -v; | |
91 } | |
92 | |
93 while (v != 0) | |
94 { | |
95 ret = cast(char)(v % 10 + '0') ~ ret; | |
96 v = cast(long)(v / 10); | |
97 } | |
98 | |
99 if (neg) | |
100 ret = "-" ~ ret; | |
101 | |
102 return ret; | |
103 } | |
104 | |
105 template ToString(long i) | |
106 { | |
107 const string ToString = __toString(i); | |
108 } | |
109 | |
110 //TODO: should be in the standard library | |
111 struct STuple(A...) | |
112 { | |
113 static string genSTuple() | |
114 { | |
115 string r = ""; | |
116 foreach (i, e; A) | |
117 r ~= A[i].stringof ~ " _" ~ ToString!(i) ~ ";"; | |
118 return r; | |
119 } | |
120 | |
121 mixin (genSTuple); | |
122 template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; | |
123 } | |
124 | |
253 | 125 enum SignalEventId |
126 { | |
127 firstSlotConnected, | |
128 lastSlotDisconnected | |
129 } | |
1 | 130 |
131 public class SignalException : Exception | |
132 { | |
133 this(char[] msg) | |
134 { | |
135 super(msg); | |
136 } | |
137 } | |
138 | |
139 struct Fn | |
140 { | |
141 void* funcptr; | |
142 | |
143 static typeof(*this) opCall(R, A...)(R function(A) fn) | |
144 { | |
145 typeof(*this) r; | |
146 r.funcptr = fn; | |
147 return r; | |
148 } | |
149 | |
150 template call(R) | |
151 { | |
152 R call(A...)(A args) | |
153 { | |
154 alias R function(A) Fn; | |
155 return (cast(Fn)funcptr)(args); | |
156 } | |
157 } | |
158 | |
159 S get(S)() | |
160 { | |
161 static assert (is(typeof(*S.init) == function)); | |
162 return cast(S)funcptr; | |
163 } | |
164 } | |
165 | |
166 struct Dg | |
167 { | |
168 void* context; | |
169 void* funcptr; | |
170 | |
171 static typeof(*this) opCall(R, A...)(R delegate(A) dg) | |
172 { | |
173 typeof(*this) r; | |
174 r.context = dg.ptr; | |
175 r.funcptr = dg.funcptr; | |
176 return r; | |
177 } | |
178 | |
179 template call(R) | |
180 { | |
181 R call(A...)(A args) | |
182 { | |
183 R delegate(A) dg; // BUG: parameter storage classes are ignored | |
184 dg.ptr = context; | |
185 dg.funcptr = cast(typeof(dg.funcptr))funcptr; | |
186 return dg(args); | |
187 } | |
188 } | |
189 | |
190 S get(S)() | |
191 { | |
192 static assert (is(S == delegate)); | |
193 S r; | |
194 r.ptr = context; | |
195 r.funcptr = cast(typeof(r.funcptr))funcptr; | |
196 return r; | |
197 } | |
198 } | |
199 | |
200 struct Slot(R) | |
201 { | |
202 alias R Receiver; | |
203 | |
204 Receiver receiver; | |
205 Dg invoker; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
206 ConnectionFlags flags; |
1 | 207 |
208 static if (is(Receiver == Dg)) | |
209 { | |
210 static const isDelegate = true; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
211 |
1 | 212 bool isDisposed() |
213 { | |
214 return !receiver.funcptr; | |
215 } | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
216 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
217 void dispose() |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
218 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
219 receiver.funcptr = null; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
220 receiver.context = null; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
221 } |
1 | 222 |
223 Object getObject() | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
224 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
225 return flags & ConnectionFlags.NoObject || !receiver.context |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
226 ? null : _d_toObject(receiver.context); |
1 | 227 } |
228 } | |
229 else | |
230 static const isDelegate = false; | |
231 } | |
232 | |
233 enum SlotListId | |
234 { | |
235 Func, // function pointers | |
236 Weak, // object delegates stored in C heap | |
237 Strong // delegates stored in GC heap | |
238 } | |
239 | |
240 /** | |
241 Used to specify the type of a signal-to-slot connection. | |
242 | |
243 Examples: | |
244 ---- | |
245 class Sender | |
246 { | |
247 mixin Signal!("changed"); | |
248 void change() | |
249 { | |
250 changed.emit; | |
251 } | |
252 } | |
253 | |
254 | |
255 class Receiver | |
256 { | |
257 void alarm() {} | |
258 } | |
259 | |
260 void main() | |
261 { | |
262 auto s = new Sender; | |
263 auto r = new Receiver; | |
264 s.changed.connect(&r.alarm); // now s weakly references r | |
265 | |
266 r = null; | |
267 // collect garbage (assume there is no more reachable pointers | |
268 // to the receiver and it gets finalized) | |
269 ... | |
270 | |
271 s.change; | |
272 // weak reference to the receiving object | |
273 // has been removed from the sender's connection lists. | |
274 | |
275 r = new Receiver; | |
276 s.changed.connect(&r.alarm, ConnectionFlags.Strong); | |
277 | |
278 r = null; | |
279 // collect garbage | |
280 ... | |
281 // the receiving object has not been finalized because s strongly references it. | |
282 | |
283 s.change; // the receiver is called. | |
284 delete r; | |
285 s.change; // the receiver is disconnected from the sender. | |
286 | |
287 static void foo() | |
288 { | |
289 } | |
290 | |
291 s.changed.connect(&foo); | |
292 s.changed.emit; // foo is called. | |
293 s.changed.disconnect(&foo); // must be explicitly disconnected. | |
294 | |
295 void bar() | |
296 { | |
297 } | |
298 | |
299 // ConnectionFlags.NoObject must be specified for delegates | |
300 // to non-static local functions or struct member functions. | |
301 s.changed.connect(&bar, ConnectionFlags.NoObject); | |
302 s.changed.emit; // bar is called. | |
303 s.changed.disconnect(&bar); // must be explicitly disconnected. | |
304 } | |
305 ---- | |
306 */ | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
307 public enum ConnectionFlags : ubyte |
1 | 308 { |
309 /// | |
310 None, | |
311 /** | |
312 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified). | |
313 If the signal receiver is not a function pointer or a delegate referencing a D class instance. | |
314 the sender will not be notified when the receiving object is deleted and emitting the signal | |
315 connected to that receiving object will result in undefined behavior. | |
316 */ | |
317 Weak = 0x0001, | |
318 /** | |
319 The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified). | |
320 */ | |
321 Strong = 0x0002, | |
322 /** | |
323 Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance. | |
324 */ | |
325 NoObject = 0x0004 | |
326 | |
327 // Queued = 0x0004, | |
328 // BlockingQueued = 0x0008 | |
329 } | |
330 | |
331 | |
332 struct SlotList(SlotT, bool strong = false) | |
333 { | |
334 alias SlotT SlotType; | |
335 SlotType[] data; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
336 |
1 | 337 void length(size_t length) |
338 { | |
339 static if (strong) | |
340 data.length = length; | |
341 else | |
342 realloc(data, length); | |
343 } | |
344 | |
345 SlotType* add(SlotType slot) | |
346 { | |
347 auto oldLen = data.length; | |
348 length = oldLen + 1; | |
349 auto p = &data[oldLen]; | |
350 *p = slot; | |
351 return p; | |
352 } | |
353 | |
354 SlotType* get(int slotId) | |
355 { | |
356 return &data[slotId]; | |
357 } | |
358 | |
359 void remove(int slotId) | |
360 { | |
361 move(data, slotId, slotId + 1, data.length - slotId - 1); | |
362 data = data[0..$ - 1]; | |
363 } | |
364 | |
365 size_t length() | |
366 { | |
367 return data.length; | |
368 } | |
369 | |
370 void free() | |
371 { | |
372 static if (!strong) | |
373 cfree(data.ptr); | |
374 } | |
375 } | |
376 | |
253 | 377 public alias void delegate(int signalId, SignalEventId event) SignalEvent; |
1 | 378 |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
379 struct Receivers |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
380 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
381 struct Data |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
382 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
383 Object object; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
384 int refs; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
385 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
386 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
387 Data[] data; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
388 void add(Object receiver, DEvent disposeEvent) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
389 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
390 foreach (ref d; data) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
391 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
392 if (d.object is receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
393 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
394 d.refs++; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
395 return; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
396 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
397 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
398 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
399 append(data, Data(receiver, 1)); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
400 rt_attachDisposeEvent(receiver, disposeEvent); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
401 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
402 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
403 void remove(Object receiver, DEvent disposeEvent) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
404 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
405 foreach (i, ref d; data) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
406 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
407 if (d.object is receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
408 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
409 assert (d.refs); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
410 d.refs--; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
411 if (!d.refs) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
412 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
413 .erase(data, i); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
414 rt_detachDisposeEvent(receiver, disposeEvent); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
415 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
416 return; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
417 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
418 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
419 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
420 assert (false); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
421 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
422 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
423 // remove all refarences for receiver, receiver has been disposed |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
424 void removeAll(Object receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
425 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
426 foreach (i, ref d; data) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
427 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
428 if (d.object is receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
429 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
430 .erase(data, i); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
431 return; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
432 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
433 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
434 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
435 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
436 // remove all references for all receivers, detaching dispose events |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
437 void free(DEvent disposeEvent) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
438 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
439 foreach (i, ref d; data) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
440 rt_detachDisposeEvent(d.object, disposeEvent); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
441 cfree(data.ptr); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
442 data = null; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
443 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
444 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
445 |
1 | 446 struct SignalConnections |
447 { | |
448 bool isInUse; | |
449 | |
450 STuple!( | |
451 SlotList!(Slot!(Fn)), | |
452 SlotList!(Slot!(Dg)), | |
453 SlotList!(Slot!(Dg), true) | |
454 ) slotLists; | |
455 | |
456 STuple!( | |
457 Fn[], | |
458 Dg[] | |
459 ) delayedDisconnects; | |
460 | |
461 void addDelayedDisconnect(Fn r) | |
462 { | |
463 delayedDisconnects.at!(0) ~= r; | |
464 } | |
465 | |
466 void addDelayedDisconnect(Dg r) | |
467 { | |
468 delayedDisconnects.at!(1) ~= r; | |
469 } | |
470 | |
471 SlotListType!(slotListId)* getSlotList(int slotListId)() | |
472 { | |
473 return &slotLists.tupleof[slotListId]; | |
474 } | |
475 | |
476 bool hasSlots() | |
477 { | |
478 foreach(i, e; slotLists.tupleof) | |
479 { | |
480 if (slotLists.tupleof[i].length) | |
481 return true; | |
482 } | |
483 return false; | |
484 } | |
485 | |
486 int slotCount() | |
487 { | |
488 int count; | |
489 foreach(i, e; slotLists.tupleof) | |
490 count += slotLists.at!(i).length; | |
491 return count; | |
492 } | |
493 | |
494 void slotListLengths(int[] lengths) | |
495 { | |
496 foreach(i, e; slotLists.tupleof) | |
497 lengths[i] = slotLists.at!(i).length; | |
498 } | |
499 | |
500 SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot) | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
501 { |
1 | 502 return getSlotList!(slotListId).add(slot); |
503 } | |
504 | |
505 void removeSlot(int slotListId)(int slotId) | |
506 { | |
507 slotLists.at!(slotListId).remove(slotId); | |
508 } | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
509 |
1 | 510 void free() |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
511 { |
1 | 512 foreach(i, e; slotLists.tupleof) |
513 { | |
514 static if (is(typeof(slotLists.at!(i).free))) | |
515 slotLists.at!(i).free; | |
516 } | |
517 } | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
518 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
519 void onReceiverDisposed(Object receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
520 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
521 foreach (i, e; slotLists.tupleof) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
522 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
523 static if (slotLists.at!(i).SlotType.isDelegate) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
524 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
525 foreach (ref slot; slotLists.at!(i).data) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
526 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
527 if (slot.getObject is receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
528 slot.dispose; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
529 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
530 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
531 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
532 } |
1 | 533 |
534 template SlotListType(int slotListId) | |
535 { | |
536 alias typeof(slotLists.tupleof)[slotListId] SlotListType; | |
537 } | |
538 | |
539 template SlotType(int slotListId) | |
540 { | |
541 alias SlotListType!(slotListId).SlotType SlotType; | |
542 } | |
543 | |
544 template ReceiverType(int slotListId) | |
545 { | |
546 alias SlotType!(slotListId).Receiver ReceiverType; | |
547 } | |
548 | |
549 static const slotListCount = slotLists.tupleof.length; | |
550 } | |
551 | |
552 | |
553 private ThreadLocal!(Object) signalSender_; | |
554 static this() | |
555 { | |
556 signalSender_ = new ThreadLocal!(Object); | |
557 } | |
558 | |
559 /** | |
560 If called from a slot, returns the object | |
561 that is emitting the signal. Otherwise, returns null. | |
562 */ | |
563 public Object signalSender() { | |
564 return signalSender_.val; | |
565 } | |
566 | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
567 public final class SignalHandler |
1 | 568 { |
569 SignalConnections[] connections; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
570 Receivers receivers; |
1 | 571 Object owner; |
572 int blocked; | |
253 | 573 |
574 SignalEvent signalEvent; | |
575 | |
1 | 576 alias SignalConnections.SlotType SlotType; |
577 alias SignalConnections.ReceiverType ReceiverType; | |
578 | |
579 public this(Object owner_) { | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
580 owner = owner_; |
1 | 581 } |
582 | |
583 private SignalConnections* getConnections(int signalId) | |
584 { | |
585 if (signalId < connections.length) | |
586 return &connections[signalId]; | |
587 return null; | |
588 } | |
589 | |
590 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
591 Dg invoker, ConnectionFlags flags) |
1 | 592 { |
593 if (signalId >= connections.length) | |
594 connections.length = signalId + 1; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
595 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker, flags)); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
596 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
597 static if (slot.isDelegate) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
598 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
599 if (!(flags & ConnectionFlags.NoObject)) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
600 receivers.add(_d_toObject(receiver.context), &onReceiverDisposed); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
601 } |
1 | 602 |
253 | 603 if (signalEvent && connections[signalId].slotCount == 1) |
604 signalEvent(signalId, SignalEventId.firstSlotConnected); | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
605 |
1 | 606 return slot; |
607 } | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
608 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
609 void onReceiverDisposed(Object receiver) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
610 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
611 synchronized(this) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
612 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
613 foreach(ref c; connections) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
614 c.onReceiverDisposed(receiver); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
615 receivers.removeAll(receiver); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
616 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
617 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
618 |
1 | 619 private void removeSlot(int slotListId)(int signalId, int slotId) |
620 { | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
621 auto slot = connections[signalId].getSlotList!(slotListId).get(slotId); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
622 static if (slot.isDelegate) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
623 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
624 if (auto obj = slot.getObject) |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
625 receivers.remove(obj, &onReceiverDisposed); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
626 } |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
627 |
1 | 628 connections[signalId].removeSlot!(slotListId)(slotId); |
629 | |
253 | 630 if (signalEvent && !connections[signalId].slotCount) |
631 signalEvent(signalId, SignalEventId.lastSlotDisconnected); | |
1 | 632 } |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
633 |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
634 |
1 | 635 size_t slotCount(int signalId) |
636 { | |
637 synchronized(this) | |
638 { | |
639 auto con = getConnections(signalId); | |
640 if (con) | |
641 return con.slotCount; | |
642 return 0; | |
643 } | |
644 } | |
645 | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
646 void connect(Receiver)(int signalId, Receiver receiver, |
1 | 647 Dg invoker, ConnectionFlags flags) |
648 { | |
649 synchronized(this) | |
650 { | |
651 static if (is(typeof(receiver.context))) | |
652 { | |
653 Object obj; | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
654 if ((flags & ConnectionFlags.NoObject)) |
1 | 655 { |
656 // strong by default | |
657 if (flags & ConnectionFlags.Weak) | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
658 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); |
1 | 659 else |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
660 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); |
1 | 661 } |
662 else | |
663 { | |
664 // weak by default | |
665 if (flags & ConnectionFlags.Strong) | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
666 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); |
1 | 667 else |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
668 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); |
1 | 669 } |
670 } | |
671 else | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
672 { |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
673 flags |= ConnectionFlags.NoObject; |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
674 addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags); |
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
675 } |
1 | 676 } |
677 } | |
678 | |
679 void disconnect(Receiver)(int signalId, Receiver receiver) | |
680 { | |
681 synchronized(this) | |
682 { | |
683 auto cons = getConnections(signalId); | |
684 if (!cons) | |
685 return; | |
686 | |
687 // if called from a slot being executed by this signal, delay disconnection | |
688 // until all slots has been called. | |
689 if (cons.isInUse) | |
690 { | |
691 cons.addDelayedDisconnect(receiver); | |
692 return; | |
693 } | |
694 | |
695 TOP: | |
696 foreach (slotListId, e; cons.slotLists.tupleof) | |
697 { | |
698 /// COMPILER BUG: ReceiverType is evaluated to expression instead of type. | |
699 static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver)) | |
700 { | |
701 auto slotList = cons.getSlotList!(slotListId); | |
702 for (int slotId; slotId < slotList.length;) | |
703 { | |
704 auto slot = slotList.get(slotId); | |
705 static if (slot.isDelegate) | |
706 { | |
707 if (slot.isDisposed) | |
708 { | |
709 removeSlot!(slotListId)(signalId, slotId); | |
710 continue; | |
711 } | |
712 } | |
713 | |
714 if (slot.receiver == receiver) | |
715 { | |
716 removeSlot!(slotListId)(signalId, slotId); | |
717 break TOP; | |
718 } | |
719 | |
720 slotId++; | |
721 } | |
722 } | |
723 } | |
724 } | |
725 } | |
726 | |
727 void emit(A...)(size_t signalId, A args) | |
728 { | |
729 synchronized(this) | |
730 { | |
731 if (signalId >= connections.length || blocked) | |
732 return; | |
733 auto cons = &connections[signalId]; | |
734 | |
735 if (cons.hasSlots) | |
736 { | |
737 { | |
738 cons.isInUse = true; | |
739 signalSender_.val = owner; | |
740 scope(exit) | |
741 { | |
742 cons.isInUse = false; | |
743 signalSender_.val = null; | |
744 } | |
745 | |
746 // Store the lengths to avoid calling new slots | |
747 // connected in the slots being called. | |
748 // dmd bug: int[cons.slotListCount] fails | |
749 static const c = cons.slotListCount; | |
750 int[c] lengths = void; | |
751 cons.slotListLengths(lengths); | |
752 | |
753 foreach (slotListId, e; cons.slotLists.tupleof) | |
754 { | |
755 auto slotList = cons.getSlotList!(slotListId); | |
756 for (size_t slotId; slotId < lengths[slotListId];) | |
757 { | |
758 auto slot = slotList.get(slotId); | |
759 static if (slot.isDelegate) | |
760 { | |
761 if (slot.isDisposed) | |
762 { | |
763 removeSlot!(slotListId)(signalId, slotId); | |
764 lengths[slotListId]--; | |
765 continue; | |
766 } | |
767 } | |
768 | |
769 slot.invoker.call!(void)(slot.receiver, args); | |
770 ++slotId; | |
771 } | |
772 } | |
773 } | |
774 | |
775 | |
776 // process delayed disconnects if any | |
777 foreach(i, e; cons.delayedDisconnects.tupleof) | |
778 { | |
779 if (cons.delayedDisconnects.at!(i).length) | |
780 { | |
781 foreach (d; cons.delayedDisconnects.at!(i)) | |
782 disconnect(signalId, d); | |
783 cons.delayedDisconnects.at!(i).length = 0; | |
784 } | |
785 } | |
786 } | |
787 } | |
788 } | |
789 | |
790 // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments | |
791 private void invokeSlot(S, Receiver, A...)(Receiver r, A args) | |
792 { | |
793 r.get!(S)()(args[0..ParameterTupleOf!(S).length]); | |
794 } | |
795 | |
796 void blockSignals() | |
797 { | |
798 synchronized(this) | |
799 blocked++; | |
800 } | |
801 | |
802 void unblockSignals() | |
803 { | |
804 synchronized(this) | |
805 { | |
806 if(!blocked) | |
807 throw new SignalException("Signals are not blocked"); | |
808 blocked--; | |
809 } | |
810 } | |
811 | |
812 ~this() | |
813 { | |
276
501128ac7a2c
signals cleaned up correctly on receiver destruction
maxter
parents:
253
diff
changeset
|
814 receivers.free(&onReceiverDisposed); |
1 | 815 foreach(ref c; connections) |
816 c.free; | |
817 } | |
818 } | |
819 | |
820 //TODO: this could be avoided if named mixins didn't suck. | |
821 public struct SignalOps(int sigId, A...) | |
822 { | |
823 private SignalHandler sh; | |
824 enum { signalId = sigId } | |
825 | |
826 void connect(R, B...)(R function(B) fn, ConnectionFlags flags = ConnectionFlags.None) | |
827 { | |
828 alias CheckSlot!(typeof(fn), A) check; | |
829 auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, A)); | |
830 sh.connect(signalId, Fn(fn), invoker, flags); | |
831 } | |
832 | |
833 void connect(R, B...)(R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None) | |
834 { | |
835 alias CheckSlot!(typeof(dg), A) check; | |
836 auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, A)); | |
837 sh.connect(signalId, Dg(dg), invoker, flags); | |
838 } | |
839 | |
840 void disconnect(R, B...)(R function(B) fn) | |
841 { | |
842 sh.disconnect(signalId, Fn(fn)); | |
843 } | |
844 | |
845 void disconnect(R, B...)(R delegate(B) dg) | |
846 { | |
847 sh.disconnect(signalId, Dg(dg)); | |
848 } | |
849 | |
850 void emit(A args) | |
851 { | |
852 sh.emit(signalId, args); | |
853 } | |
854 | |
855 debug size_t slotCount() | |
856 { | |
857 return sh.slotCount(signalId); | |
858 } | |
859 } | |
860 | |
861 template CheckSlot(Slot, A...) | |
862 { | |
863 static assert(ParameterTupleOf!(Slot).length <= A.length, "Slot " ~ ParameterTypeTuple!(Slot).stringof ~ | |
864 " has more prameters than signal " ~ A.stringof); | |
865 alias CheckSlotImpl!(Slot, 0, A) check; | |
866 } | |
867 | |
868 template CheckSlotImpl(Slot, int i, A...) | |
869 { | |
870 alias ParameterTupleOf!(Slot) SlotArgs; | |
871 static if (i < SlotArgs.length) | |
872 { | |
873 static assert (is(SlotArgs[i] : A[i]), "Argument " ~ __toString(i) ~ | |
874 ":" ~ A[i].stringof ~ " of signal " ~ A.stringof ~ " is not implicitly convertible to parameter " | |
875 ~ SlotArgs[i].stringof ~ " of slot " ~ SlotArgs.stringof); | |
876 alias CheckSlotImpl!(Slot, i + 1, A) next; | |
877 } | |
878 } | |
879 | |
880 public template SignalHandlerOps() | |
881 { | |
882 static assert (is(typeof(this.signalHandler)), | |
883 "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes"); | |
884 | |
885 protected: | |
886 SignalHandler signalHandler_; // manages signal-to-slot connections | |
887 | |
888 final SignalHandler signalHandler() | |
889 { | |
890 if (!signalHandler_) | |
891 { | |
892 signalHandler_ = new SignalHandler(this); | |
893 onSignalHandlerCreated(signalHandler_); | |
894 } | |
895 return signalHandler_; | |
896 } | |
897 | |
898 void onSignalHandlerCreated(ref SignalHandler sh) | |
899 { | |
900 } | |
901 | |
902 public: | |
903 final void blockSignals() | |
904 { | |
905 signalHandler.blockSignals(); | |
906 } | |
907 | |
908 final void unblockSignals() | |
909 { | |
910 signalHandler.unblockSignals(); | |
911 } | |
278 | 912 |
913 void connect(string signalName, T, R, B...)(T sender, R function(B) dg, ConnectionFlags flags = ConnectionFlags.None) | |
914 { | |
915 alias findSymbol!(SignalQualifier, T, signalName) sig; | |
916 alias CheckSlot!(typeof(fn), sig[2].at) check; | |
917 auto sh = sender.signalHandler(); | |
918 auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, sig[2].at)); | |
919 sh.connect(sig[1], Fn(fn), invoker, flags); | |
920 } | |
921 | |
922 void connect(string signalName, T, R, B...)(T sender, R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None) | |
923 { | |
924 alias findSymbol!(SignalQualifier, T, signalName) sig; | |
925 alias CheckSlot!(typeof(dg), sig[2].at) check; | |
926 auto sh = sender.signalHandler(); | |
927 auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, sig[2].at)); | |
928 sh.connect(sig[1], Dg(dg), invoker, flags); | |
929 } | |
930 | |
931 void disconnect(string signalName, T, R, B...)(T sender, R function(B) fn) | |
932 { | |
933 alias findSymbol!(SignalQualifier, T, signalName) sig; | |
934 auto sh = sender.signalHandler(); | |
935 sh.disconnect(sig[1], Fn(fn)); | |
936 } | |
937 | |
938 void disconnect(string signalName, T, R, B...)(T sender, R delegate(B) dg) | |
939 { | |
940 alias findSymbol!(SignalQualifier, T, signalName) sig; | |
941 auto sh = sender.signalHandler(); | |
942 sh.disconnect(sig[1], Dg(dg)); | |
943 } | |
944 | |
945 debug size_t slotCount(string signalName, T)(T sender) | |
946 { | |
947 alias findSymbol!(SignalQualifier, T, signalName) sig; | |
948 auto sh = sender.signalHandler(); | |
949 return sh.slotCount(sig[1]); | |
950 } | |
1 | 951 } |
952 | |
953 /** | |
278 | 954 New implementation. |
955 */ | |
956 | |
957 public bool startsWith(T)(T[] source, T[] pattern) | |
958 { | |
959 return source.length >= pattern.length && source[0 .. pattern.length] == pattern[]; | |
960 } | |
961 | |
962 template TupleWrapper(A...) { alias A at; } | |
963 | |
964 string joinArgs(A...)() | |
965 { | |
966 string res = ""; | |
967 static if(A.length) | |
968 { | |
969 res = A[0].stringof; | |
970 foreach(k; A[1..$]) | |
971 res ~= "," ~ k.stringof; | |
972 } | |
973 return res; | |
974 } | |
975 | |
976 struct SignalQualifier | |
977 { | |
978 const string staticSymbolPrefix = "__signal"; | |
979 const string name = "signal"; | |
980 | |
981 static bool matches(alias source, string sigName)() | |
982 { | |
983 return startsWith(source.at[0], sigName); | |
984 } | |
985 } | |
986 | |
987 template staticSymbolName(Qualifier, int id) | |
988 { | |
989 const string staticSymbolName = Qualifier.staticSymbolPrefix ~ ToString!(id); | |
990 } | |
991 | |
992 template signatureString(string name, A...) | |
993 { | |
994 const string signatureString = name ~ "(" ~ joinArgs!(A) ~ ")"; | |
995 } | |
996 | |
997 template findSymbolImpl(Qualifier, C, int id, string key) | |
998 { | |
999 static if ( is(typeof(mixin("C." ~ staticSymbolName!(Qualifier, id)))) ) | |
1000 { | |
1001 mixin ("alias C." ~ staticSymbolName!(Qualifier, id) ~ " current;"); | |
1002 static if ( Qualifier.matches!(TupleWrapper!(current), key)() ) { | |
1003 alias current result; | |
1004 } | |
1005 else | |
1006 alias findSymbolImpl!(Qualifier, C, id + 1, key).result result; | |
1007 } | |
1008 else | |
1009 { | |
1010 static assert(0, Qualifier.name ~ " " ~ key ~ " not found."); | |
1011 } | |
1012 } | |
1013 | |
1014 template findSymbol(Qualifier, C, string key) | |
1015 { | |
1016 alias findSymbolImpl!(Qualifier, C, 0, key).result findSymbol; | |
1017 } | |
1018 | |
1019 | |
1020 /** | |
1 | 1021 Examples: |
1022 ---- | |
1023 struct Args | |
1024 { | |
1025 bool cancel; | |
1026 } | |
1027 | |
1028 class C | |
1029 { | |
1030 private int _x; | |
1031 // reference parameters are not supported yet, | |
1032 // so we pass arguments by pointer. | |
1033 mixin Signal!("xChanging", int, Args*); | |
1034 mixin Signal!("xChanged"); | |
1035 | |
1036 void x(int v) | |
1037 { | |
1038 if (v != _x) | |
1039 { | |
1040 Args args; | |
1041 xChanging.emit(v, &args); | |
1042 if (!args.cancel) | |
1043 { | |
1044 _x = v; | |
1045 xChanged.emit; | |
1046 } | |
1047 } | |
1048 } | |
1049 } | |
1050 ---- | |
1051 */ | |
1052 template Signal(string name, A...) | |
1053 { | |
1054 mixin SignalImpl!(0, name, A); | |
1055 } | |
1056 | |
1057 template SignalImpl(int index, string name, A...) | |
1058 { | |
1059 static if (is(typeof(mixin(typeof(this).stringof ~ ".__sig" ~ ToString!(index))))) | |
1060 mixin SignalImpl!(index + 1, name, A); | |
1061 else | |
1062 { | |
1063 // mixed-in once | |
1064 static if (!is(typeof(this.signalHandler))) | |
1065 { | |
1066 mixin SignalHandlerOps; | |
1067 } | |
1068 mixin("private static const int __sig" ~ ToString!(index) ~ " = " ~ ToString!(index) ~ ";"); | |
279 | 1069 // mixin("public alias Tuple!(\"" ~ signatureString!(name, A) ~ "\", index, TupleWrapper!(A)) __signal" ~ ToString!(index) ~ ";"); |
1 | 1070 mixin("SignalOps!(" ~ ToString!(index) ~ ", A) " ~ name ~ "(){ return SignalOps!(" |
1071 ~ ToString!(index) ~ ", A)(signalHandler); }"); | |
1072 } | |
1073 } | |
1074 | |
1075 extern(C) alias void function(void*) SlotConnector; | |
1076 | |
1077 debug (UnitTest) | |
1078 { | |
1079 class A | |
1080 { | |
1081 mixin Signal!("scorched", int); | |
1082 | |
1083 int signalId1 = -1; | |
1084 int signalId2 = -1; | |
1085 | |
1086 void onFirstConnect(int sId) | |
1087 { | |
1088 signalId1 = sId; | |
1089 } | |
1090 | |
1091 void onLastDisconnect(int sId) | |
1092 { | |
1093 signalId2 = sId; | |
1094 } | |
1095 | |
1096 this() | |
1097 { | |
1098 signalHandler.firstSlotConnected = &onFirstConnect; | |
1099 signalHandler.lastSlotDisconnected = &onLastDisconnect; | |
1100 } | |
1101 } | |
1102 | |
1103 class B : A | |
1104 { | |
1105 mixin Signal!("booed", int); | |
1106 | |
1107 int bazSum; | |
1108 void baz(int i) | |
1109 { | |
1110 bazSum += i; | |
1111 } | |
1112 } | |
1113 | |
1114 class C : A | |
1115 { | |
1116 mixin Signal!("cooked"); | |
1117 } | |
1118 } | |
1119 | |
1120 unittest | |
1121 { | |
1122 static int fooSum; | |
1123 static int barSum; | |
1124 | |
1125 static void foo(int i) | |
1126 { | |
1127 fooSum += i; | |
1128 } | |
1129 | |
1130 void bar(long i) | |
1131 { | |
1132 barSum += i; | |
1133 } | |
1134 | |
1135 auto a = new A; | |
1136 auto b = new B; | |
1137 auto c = new C; | |
1138 assert(b.scorched.signalId == 0); | |
1139 assert(b.booed.signalId == 1); | |
1140 assert(c.cooked.signalId == 1); | |
1141 | |
1142 auto sh = b.signalHandler; | |
1143 | |
1144 b.scorched.connect(&foo); | |
1145 assert(sh.connections.length == 1); | |
1146 assert(b.signalId1 == 0); | |
1147 auto scCons = &sh.connections[0]; | |
1148 | |
1149 assert(scCons.getSlotList!(SlotListId.Func).length == 1); | |
1150 b.scorched.emit(1); | |
1151 assert(fooSum == 1); | |
1152 | |
1153 b.scorched.connect(&bar, ConnectionFlags.NoObject); | |
1154 assert(sh.connections.length == 1); | |
1155 assert(scCons.getSlotList!(SlotListId.Strong).length == 1); | |
1156 b.scorched.emit(1); | |
1157 assert (fooSum == 2 && barSum == 1); | |
1158 | |
1159 b.scorched.connect(&b.baz); | |
1160 assert(scCons.getSlotList!(SlotListId.Weak).length == 1); | |
1161 b.scorched.emit(1); | |
1162 assert (fooSum == 3 && barSum == 2 && b.bazSum == 1); | |
1163 | |
1164 b.scorched.disconnect(&bar); | |
1165 assert(scCons.slotCount == 2); | |
1166 b.scorched.disconnect(&b.baz); | |
1167 assert(scCons.slotCount == 1); | |
1168 b.scorched.disconnect(&foo); | |
1169 assert(scCons.slotCount == 0); | |
1170 assert(b.signalId2 == 0); | |
1171 | |
1172 fooSum = 0; | |
1173 void connectFoo() | |
1174 { | |
1175 b.scorched.connect(&foo); | |
1176 b.scorched.disconnect(&connectFoo); | |
1177 } | |
1178 | |
1179 b.scorched.connect(&connectFoo, ConnectionFlags.NoObject); | |
1180 b.scorched.emit(1); | |
1181 assert(scCons.getSlotList!(SlotListId.Func).length == 1); | |
1182 assert(scCons.getSlotList!(SlotListId.Strong).length == 0); | |
1183 assert(!fooSum); | |
1184 | |
1185 auto r = new B(); | |
1186 b.scorched.connect(&r.baz); | |
1187 assert(scCons.getSlotList!(SlotListId.Weak).length == 1); | |
1188 b.scorched.emit(1); | |
1189 assert(r.bazSum == 1); | |
1190 assert(fooSum == 1); | |
1191 | |
1192 delete(r); | |
1193 assert(scCons.getSlotList!(SlotListId.Weak).length == 1); | |
1194 b.scorched.emit(1); | |
1195 assert(scCons.getSlotList!(SlotListId.Weak).length == 0); | |
1196 } |