comparison qt/d2/qt/Signal.d @ 16:6faf3d3cb95e

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