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