comparison d1/qtd/Signal.d @ 311:8674fd5f34f4 lifetime

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