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