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