comparison qt/d2/qt/Signal.d @ 288:f9559a957be9 signals

new signals and slots implementation
author eldar
date Sun, 08 Nov 2009 19:28:01 +0000
parents 1f6923c8cba0
children c9d1aac290e9
comparison
equal deleted inserted replaced
287:b6984b290e46 288:f9559a957be9
10 * 10 *
11 */ 11 */
12 module qt.Signal; 12 module qt.Signal;
13 13
14 public import qt.QGlobal; 14 public import qt.QGlobal;
15 public import 15 import qt.qtd.MetaMarshall;
16 std.metastrings, 16
17 std.typetuple;
18 import core.stdc.stdlib : crealloc = realloc, cfree = free; 17 import core.stdc.stdlib : crealloc = realloc, cfree = free;
19 import core.stdc.string : memmove; 18 import core.stdc.string : memmove;
20 import 19 import
20 core.thread,
21 core.exception,
22 std.algorithm;
23
24 public import
25 std.typetuple,
21 std.traits, 26 std.traits,
22 core.thread, 27 std.conv,
23 core.exception; 28 std.string,
24 29 std.metastrings;
25 private: // private by default 30
26 31
27 alias void delegate(Object) DEvent; 32 // returns name, arguments or tuple of the function depending on type parameter
28 33 enum {_Name, _Tuple, _Args}
29 extern(C) void rt_attachDisposeEvent(Object o, DEvent e); 34 string getFunc(int type)(string fullName)
30 extern(C) void rt_detachDisposeEvent(Object o, DEvent e); 35 {
31 extern(C) Object _d_toObject(void* p); 36 int pos = 0;
32 37 foreach(i, c; fullName)
33 void realloc(T)(ref T[] a, size_t length) 38 if (c == '(')
34 { 39 static if (type == _Tuple)
35 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; 40 return fullName[i..$];
36 if (!a.ptr) 41 else if (type == _Name)
37 new OutOfMemoryError(__FILE__, __LINE__); 42 return fullName[0..i];
38 } 43 else if (type == _Args)
39 44 for(int j = fullName.length-1;; j--)
40 45 if(fullName[j] == ')')
41 void append(T)(ref T[] a, T element) 46 return fullName[i+1 .. j];
42 { 47 return null;
43 auto newLen = a.length + 1; 48 }
44 a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; 49
45 if (!a.ptr) 50 /** The beast that takes string representation of function arguments
46 new OutOfMemoryError(__FILE__, __LINE__); 51 * and returns an array of default values it doesn't check if arguments
47 a[newLen - 1] = element; 52 * without default values follow the arguments with default values for
48 } 53 * simplicity. It is done by mixing in an delegate alias.
49 54 */
50 void move(T)(ref T[] a, size_t src, size_t dest, size_t length) 55 string[] defaultValues(string signature)
51 { 56 {
52 if (a.length > 1) 57 int braces = 0;
53 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); 58 bool inDefaultValue = false;
54 } 59 bool inStringLiteral = false;
55 60 string[] res;
56 // COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts 61 int startValue = 0;
57 // with Array.remove 62
58 void erase(T)(ref T[] a, size_t i) 63 if(strip(signature).length == 0)
59 { 64 return res;
60 auto newLen = a.length - 1; 65
61 move(a, i + 1, i, newLen); 66 foreach (i,c; signature)
62 realloc(a, newLen); 67 {
63 } 68 if(!inStringLiteral)
64
65 version (QtdUnittest)
66 {
67 unittest
68 {
69 int[] a;
70 realloc(a, 16);
71 assert(a.length == 16);
72 foreach (i, ref e; a)
73 e = i;
74 realloc(a, 4096);
75 assert(a.length == 4096);
76 foreach (i, e; a[0..16])
77 assert(e == i);
78 cfree(a.ptr);
79 }
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 enum SignalEventId
98 {
99 firstSlotConnected,
100 lastSlotDisconnected
101 }
102
103 public class SignalException : Exception
104 {
105 this(string msg)
106 {
107 super(msg);
108 }
109 }
110
111 struct Fn
112 {
113 void* funcptr;
114
115 static typeof(this) opCall(R, A...)(R function(A) fn)
116 {
117 typeof(this) r;
118 r.funcptr = fn;
119 return r;
120 }
121
122 template call(R)
123 {
124 R call(A...)(A args)
125 { 69 {
126 alias R function(A) Fn; 70 if(c == '{' || c =='(')
127 return (cast(Fn)funcptr)(args); 71 braces++;
72 else if(c == '}' || c ==')')
73 braces--;
128 } 74 }
129 } 75
130 76 if(c == '\"' || c == '\'')
131 S get(S)()
132 {
133 static assert (is(typeof(*S.init) == function));
134 return cast(S)funcptr;
135 }
136 }
137
138 struct Dg
139 {
140 void* context;
141 void* funcptr;
142
143 static typeof(this) opCall(R, A...)(R delegate(A) dg)
144 {
145 typeof(this) r;
146 r.context = dg.ptr;
147 r.funcptr = dg.funcptr;
148 return r;
149 }
150
151 template call(R)
152 {
153 R call(A...)(A args)
154 { 77 {
155 R delegate(A) dg; // BUG: parameter storage classes are ignored 78 if (inStringLiteral)
156 dg.ptr = context; 79 {
157 dg.funcptr = cast(typeof(dg.funcptr))funcptr; 80 if(signature[i-1] != '\\')
158 return dg(args); 81 inStringLiteral = false;
159 } 82 }
160 } 83 else
161 84 {
162 S get(S)() 85 inStringLiteral = true;
163 {
164 static assert (is(S == delegate));
165 S r;
166 r.ptr = context;
167 r.funcptr = cast(typeof(r.funcptr))funcptr;
168 return r;
169 }
170 }
171
172 struct Slot(R)
173 {
174 alias R Receiver;
175
176 Receiver receiver;
177 Dg invoker;
178 ConnectionFlags flags;
179
180 static if (is(Receiver == Dg))
181 {
182 static const isDelegate = true;
183
184 bool isDisposed()
185 {
186 return !receiver.funcptr;
187 }
188
189 void dispose()
190 {
191 receiver.funcptr = null;
192 receiver.context = null;
193 }
194
195 Object getObject()
196 {
197 return flags & ConnectionFlags.NoObject || !receiver.context
198 ? null : _d_toObject(receiver.context);
199 }
200 }
201 else
202 static const isDelegate = false;
203 }
204
205 enum SlotListId
206 {
207 Func, // function pointers
208 Weak, // object delegates stored in C heap
209 Strong // delegates stored in GC heap
210 }
211
212 /**
213 Used to specify the type of a signal-to-slot connection.
214
215 Examples:
216 ----
217 class Sender
218 {
219 mixin Signal!("changed");
220 void change()
221 {
222 changed.emit;
223 }
224 }
225
226
227 class Receiver
228 {
229 void alarm() {}
230 }
231
232 void main()
233 {
234 auto s = new Sender;
235 auto r = new Receiver;
236 s.changed.connect(&r.alarm); // now s weakly references r
237
238 r = null;
239 // collect garbage (assume there is no more reachable pointers
240 // to the receiver and it gets finalized)
241 ...
242
243 s.change;
244 // weak reference to the receiving object
245 // has been removed from the sender's connection lists.
246
247 r = new Receiver;
248 s.changed.connect(&r.alarm, ConnectionFlags.Strong);
249
250 r = null;
251 // collect garbage
252 ...
253 // the receiving object has not been finalized because s strongly references it.
254
255 s.change; // the receiver is called.
256 delete r;
257 s.change; // the receiver is disconnected from the sender.
258
259 static void foo()
260 {
261 }
262
263 s.changed.connect(&foo);
264 s.changed.emit; // foo is called.
265 s.changed.disconnect(&foo); // must be explicitly disconnected.
266
267 void bar()
268 {
269 }
270
271 // ConnectionFlags.NoObject must be specified for delegates
272 // to non-static local functions or struct member functions.
273 s.changed.connect(&bar, ConnectionFlags.NoObject);
274 s.changed.emit; // bar is called.
275 s.changed.disconnect(&bar); // must be explicitly disconnected.
276 }
277 ----
278 */
279 public enum ConnectionFlags : ubyte
280 {
281 ///
282 None,
283 /**
284 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
285 If the signal receiver is not a function pointer or a delegate referencing a D class instance.
286 the sender will not be notified when the receiving object is deleted and emitting the signal
287 connected to that receiving object will result in undefined behavior.
288 */
289 Weak = 0x0001,
290 /**
291 The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified).
292 */
293 Strong = 0x0002,
294 /**
295 Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance.
296 */
297 NoObject = 0x0004
298
299 // Queued = 0x0004,
300 // BlockingQueued = 0x0008
301 }
302
303
304 struct SlotList(SlotT, bool strong = false)
305 {
306 alias SlotT SlotType;
307 SlotType[] data;
308
309 void length(size_t length)
310 {
311 static if (strong)
312 data.length = length;
313 else
314 realloc(data, length);
315 }
316
317 SlotType* add(SlotType slot)
318 {
319 auto oldLen = data.length;
320 length = oldLen + 1;
321 auto p = &data[oldLen];
322 *p = slot;
323 return p;
324 }
325
326 SlotType* get(int slotId)
327 {
328 return &data[slotId];
329 }
330
331 void remove(int slotId)
332 {
333 move(data, slotId, slotId + 1, data.length - slotId - 1);
334 data = data[0..$ - 1];
335 }
336
337 size_t length()
338 {
339 return data.length;
340 }
341
342 void free()
343 {
344 static if (!strong)
345 cfree(data.ptr);
346 }
347 }
348
349 public alias void delegate(int signalId, SignalEventId event) SignalEvent;
350
351 struct Receivers
352 {
353 struct Data
354 {
355 Object object;
356 int refs;
357 }
358
359 Data[] data;
360 void add(Object receiver, DEvent disposeEvent)
361 {
362 foreach (ref d; data)
363 {
364 if (d.object is receiver)
365 {
366 d.refs++;
367 return;
368 } 86 }
369 } 87 }
370 88
371 append(data, Data(receiver, 1)); 89 if (!inStringLiteral && braces == 0)
372 rt_attachDisposeEvent(receiver, disposeEvent);
373 }
374
375 void remove(Object receiver, DEvent disposeEvent)
376 {
377 foreach (i, ref d; data)
378 { 90 {
379 if (d.object is receiver) 91 if(c == '=') // found default value
380 { 92 {
381 assert (d.refs); 93 inDefaultValue = true;
382 d.refs--; 94 startValue = i+1;
383 if (!d.refs) 95 }
96 else if(c == ',') // next function argument
97 {
98 if (inDefaultValue)
384 { 99 {
385 .erase(data, i); 100 res ~= signature[startValue..i];
386 rt_detachDisposeEvent(receiver, disposeEvent); 101 inDefaultValue = false;
387 }
388 return;
389 }
390 }
391
392 assert (false);
393 }
394
395 // remove all refarences for receiver, receiver has been disposed
396 void removeAll(Object receiver)
397 {
398 foreach (i, ref d; data)
399 {
400 if (d.object is receiver)
401 {
402 .erase(data, i);
403 return;
404 }
405 }
406 }
407
408 // remove all references for all receivers, detaching dispose events
409 void free(DEvent disposeEvent)
410 {
411 foreach (i, ref d; data)
412 rt_detachDisposeEvent(d.object, disposeEvent);
413 cfree(data.ptr);
414 data = null;
415 }
416 }
417
418 struct SignalConnections
419 {
420 bool isInUse;
421
422 STuple!(
423 SlotList!(Slot!(Fn)),
424 SlotList!(Slot!(Dg)),
425 SlotList!(Slot!(Dg), true)
426 ) slotLists;
427
428 STuple!(
429 Fn[],
430 Dg[]
431 ) delayedDisconnects;
432
433 void addDelayedDisconnect(Fn r)
434 {
435 delayedDisconnects.at!(0) ~= r;
436 }
437
438 void addDelayedDisconnect(Dg r)
439 {
440 delayedDisconnects.at!(1) ~= r;
441 }
442
443 SlotListType!(slotListId)* getSlotList(int slotListId)()
444 {
445 return &slotLists.tupleof[slotListId];
446 }
447
448 bool hasSlots()
449 {
450 foreach(i, e; slotLists.tupleof)
451 {
452 if (slotLists.tupleof[i].length)
453 return true;
454 }
455 return false;
456 }
457
458 int slotCount()
459 {
460 int count;
461 foreach(i, e; slotLists.tupleof)
462 count += slotLists.at!(i).length;
463 return count;
464 }
465
466 void slotListLengths(int[] lengths)
467 {
468 foreach(i, e; slotLists.tupleof)
469 lengths[i] = slotLists.at!(i).length;
470 }
471
472 SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
473 {
474 return getSlotList!(slotListId).add(slot);
475 }
476
477 void removeSlot(int slotListId)(int slotId)
478 {
479 slotLists.at!(slotListId).remove(slotId);
480 }
481
482 void free()
483 {
484 foreach(i, e; slotLists.tupleof)
485 {
486 static if (is(typeof(slotLists.at!(i).free)))
487 slotLists.at!(i).free;
488 }
489 }
490
491 void onReceiverDisposed(Object receiver)
492 {
493 foreach (i, e; slotLists.tupleof)
494 {
495 static if (slotLists.at!(i).SlotType.isDelegate)
496 {
497 foreach (ref slot; slotLists.at!(i).data)
498 {
499 if (slot.getObject is receiver)
500 slot.dispose;
501 } 102 }
502 } 103 }
503 } 104 }
504 } 105 }
505
506 template SlotListType(int slotListId)
507 {
508 alias typeof(slotLists.tupleof)[slotListId] SlotListType;
509 }
510
511 template SlotType(int slotListId)
512 {
513 alias SlotListType!(slotListId).SlotType SlotType;
514 }
515
516 template ReceiverType(int slotListId)
517 {
518 alias SlotType!(slotListId).Receiver ReceiverType;
519 }
520
521 static const slotListCount = slotLists.tupleof.length;
522 }
523
524
525 private Object signalSender_;
526
527 /**
528 If called from a slot, returns the object
529 that is emitting the signal. Otherwise, returns null.
530 */
531 public Object signalSender() {
532 return signalSender_;
533 }
534
535 public final class SignalHandler
536 {
537 SignalConnections[] connections;
538 Receivers receivers;
539 Object owner;
540 int blocked;
541 106
542 SignalEvent signalEvent; 107 if (inDefaultValue)
543 108 res ~= signature[startValue..$];
544 alias SignalConnections.SlotType SlotType; 109
545 alias SignalConnections.ReceiverType ReceiverType; 110 return res;
546 111 }
547 public this(Object owner_) { 112
548 owner = owner_; 113 int defaultValuesLength(string[] defVals)
549 } 114 {
550 115 return defVals.length;
551 private SignalConnections* getConnections(int signalId)
552 {
553 if (signalId < connections.length)
554 return &connections[signalId];
555 return null;
556 }
557
558 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
559 Dg invoker, ConnectionFlags flags)
560 {
561 if (signalId >= connections.length)
562 connections.length = signalId + 1;
563 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker));
564
565 static if (slot.isDelegate)
566 {
567 if (!(flags & ConnectionFlags.NoObject))
568 receivers.add(_d_toObject(receiver.context), &onReceiverDisposed);
569 }
570
571 if (signalEvent && connections[signalId].slotCount == 1)
572 signalEvent(signalId, SignalEventId.firstSlotConnected);
573
574 return slot;
575 }
576
577 void onReceiverDisposed(Object receiver)
578 {
579 synchronized(this)
580 {
581 foreach(ref c; connections)
582 c.onReceiverDisposed(receiver);
583 receivers.removeAll(receiver);
584 }
585 }
586
587 private void removeSlot(int slotListId)(int signalId, int slotId)
588 {
589 auto slot = connections[signalId].getSlotList!(slotListId).get(slotId);
590 static if (slot.isDelegate)
591 {
592 if (auto obj = slot.getObject)
593 receivers.remove(obj, &onReceiverDisposed);
594 }
595
596 connections[signalId].removeSlot!(slotListId)(slotId);
597
598 if (signalEvent && !connections[signalId].slotCount)
599 signalEvent(signalId, SignalEventId.lastSlotDisconnected);
600 }
601
602 size_t slotCount(int signalId)
603 {
604 synchronized(this)
605 {
606 auto con = getConnections(signalId);
607 if (con)
608 return con.slotCount;
609 return 0;
610 }
611 }
612
613 void connect(Receiver)(int signalId, Receiver receiver,
614 Dg invoker, ConnectionFlags flags)
615 {
616 synchronized(this)
617 {
618 static if (is(typeof(receiver.context)))
619 {
620 Object obj;
621 if ((flags & ConnectionFlags.NoObject))
622 {
623 // strong by default
624 if (flags & ConnectionFlags.Weak)
625 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags);
626 else
627 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags);
628 }
629 else
630 {
631 // weak by default
632 if (flags & ConnectionFlags.Strong)
633 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags);
634 else
635 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags);
636 }
637 }
638 else
639 addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags);
640 }
641 }
642
643 void disconnect(Receiver)(int signalId, Receiver receiver)
644 {
645 synchronized(this)
646 {
647 auto cons = getConnections(signalId);
648 if (!cons)
649 return;
650
651 // if called from a slot being executed by this signal, delay disconnection
652 // until all slots has been called.
653 if (cons.isInUse)
654 {
655 cons.addDelayedDisconnect(receiver);
656 return;
657 }
658
659 TOP:
660 foreach (slotListId, e; cons.slotLists.tupleof)
661 {
662 /// COMPILER BUG: ReceiverType is evaluated to expression instead of type.
663 static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver))
664 {
665 auto slotList = cons.getSlotList!(slotListId);
666 for (int slotId; slotId < slotList.length;)
667 {
668 auto slot = slotList.get(slotId);
669 static if (slot.isDelegate)
670 {
671 if (slot.isDisposed)
672 {
673 removeSlot!(slotListId)(signalId, slotId);
674 continue;
675 }
676 }
677
678 if (slot.receiver == receiver)
679 {
680 removeSlot!(slotListId)(signalId, slotId);
681 break TOP;
682 }
683
684 slotId++;
685 }
686 }
687 }
688 }
689 }
690
691 void emit(A...)(size_t signalId, A args)
692 {
693 synchronized(this)
694 {
695 if (signalId >= connections.length || blocked)
696 return;
697 auto cons = &connections[signalId];
698
699 if (cons.hasSlots)
700 {
701 {
702 cons.isInUse = true;
703 signalSender_ = owner;
704 scope(exit)
705 {
706 cons.isInUse = false;
707 signalSender_ = null;
708 }
709
710 // Store the lengths to avoid calling new slots
711 // connected in the slots being called.
712 // dmd bug: int[cons.slotListCount] fails
713 static const c = cons.slotListCount;
714 int[c] lengths = void;
715 cons.slotListLengths(lengths);
716
717 foreach (slotListId, e; cons.slotLists.tupleof)
718 {
719 auto slotList = cons.getSlotList!(slotListId);
720 for (size_t slotId; slotId < lengths[slotListId];)
721 {
722 auto slot = slotList.get(slotId);
723 static if (slot.isDelegate)
724 {
725 if (slot.isDisposed)
726 {
727 removeSlot!(slotListId)(signalId, slotId);
728 lengths[slotListId]--;
729 continue;
730 }
731 }
732
733 slot.invoker.call!(void)(slot.receiver, args);
734 ++slotId;
735 }
736 }
737 }
738
739
740 // process delayed disconnects if any
741 foreach(i, e; cons.delayedDisconnects.tupleof)
742 {
743 if (cons.delayedDisconnects.at!(i).length)
744 {
745 foreach (d; cons.delayedDisconnects.at!(i))
746 disconnect(signalId, d);
747 cons.delayedDisconnects.at!(i).length = 0;
748 }
749 }
750 }
751 }
752 }
753
754 // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments
755 private void invokeSlot(S, Receiver, A...)(Receiver r, A args)
756 {
757 r.get!(S)()(args[0..ParameterTypeTuple!(S).length]);
758 }
759
760 void blockSignals()
761 {
762 synchronized(this)
763 blocked++;
764 }
765
766 void unblockSignals()
767 {
768 synchronized(this)
769 {
770 if(!blocked)
771 throw new SignalException("Signals are not blocked");
772 blocked--;
773 }
774 }
775
776 ~this()
777 {
778 receivers.free(&onReceiverDisposed);
779 foreach(ref c; connections)
780 c.free;
781 }
782 }
783
784 public template SignalHandlerOps()
785 {
786 static assert (is(typeof(this.signalHandler)),
787 "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes");
788
789 protected:
790 SignalHandler signalHandler_; // manages signal-to-slot connections
791
792 final SignalHandler signalHandler()
793 {
794 if (!signalHandler_)
795 {
796 signalHandler_ = new SignalHandler(this);
797 onSignalHandlerCreated(signalHandler_);
798 }
799 return signalHandler_;
800 }
801
802 void onSignalHandlerCreated(ref SignalHandler sh)
803 {
804 }
805
806 public:
807 final void blockSignals()
808 {
809 signalHandler.blockSignals();
810 }
811
812 final void unblockSignals()
813 {
814 signalHandler.unblockSignals();
815 }
816
817 template connect(string signalName, A...)
818 {
819 static void connect(T, Func)(T sender, Func func, ConnectionFlags flags = ConnectionFlags.None)
820 if (isFnOrDg!(Func))
821 {
822 alias findSignal!(T, signalName, Func, A).result sig;
823 auto sh = sender.signalHandler();
824 static if (isFn!(Func))
825 alias Fn Callable;
826 else
827 alias Dg Callable;
828 auto invoker = Dg(&sh.invokeSlot!(typeof(func), Callable, sig[2..$]));
829 sh.connect(sig[1], Callable(func), invoker, flags);
830 }
831 }
832
833 template disconnect(string signalName, A...)
834 {
835 static void connect(T, Func)(T sender, Func func, ConnectionFlags flags = ConnectionFlags.None)
836 if (isFnOrDg!(Func))
837 {
838 alias findSignal!(T, signalName, Func, A).result sig;
839 auto sh = sender.signalHandler();
840 static if (isFn!(Func))
841 alias Fn Callable;
842 else
843 alias Dg Callable;
844 sh.disconnect(sig[1], Callable(func));
845 }
846 }
847 /*
848 template slotCount(string signalName, A...)
849 {
850 debug static void slotCount(T)(T sender)
851 {
852 alias findSignal!(T, signalName, Func, A).result sig;
853 auto sh = sender.signalHandler();
854 return sh.slotCount(sig[1]);
855 }
856 }
857 */
858 } 116 }
859 117
860 /** 118 /**
861 New implementation. 119 New implementation.
862 */ 120 */
863 121
864 const string signalPrefix = "__signal"; 122
123 // need this to mark static metamethods-info whether it's generated by presence of default args
124 enum DefaultArgs
125 {
126 None, Start, Continue
127 }
128
129 // templates for extracting data from static meta-information of signals, slots or properties
130 // public alias TypeTuple!("name", index, OwnerClass, DefaultArgs.Start, ArgTypes) __signal
131 template MetaEntryName(source...)
132 {
133 enum MetaEntryName = source[0]; // name of the metaentry is the first element
134 }
135
136 template MetaEntryOwner(source...)
137 {
138 alias TupleWrapper!(source[2]).at[0] MetaEntryOwner; // class that owns the property is the third
139 // Compiler #BUG 3092 - evaluates MetaEntryOwner as a Tuple with one element
140 }
141
142 template MetaEntryArgs(source...)
143 {
144 alias source[4 .. $] MetaEntryArgs; // arguments-tuple starts from the fourth position
145 }
865 146
866 template TupleWrapper(A...) { alias A at; } 147 template TupleWrapper(A...) { alias A at; }
867 148
868 template isDg(Dg) 149 template isDg(Dg)
869 { 150 {
953 else 234 else
954 enum SigBySignPred = false; 235 enum SigBySignPred = false;
955 } 236 }
956 } 237 }
957 238
239 template ByOwner(Owner)
240 {
241 template ByOwner(source...)
242 {
243 enum ByOwner = is(MetaEntryOwner!source == Owner);
244 }
245 }
246
958 template staticSymbolName(string prefix, int id) 247 template staticSymbolName(string prefix, int id)
959 { 248 {
960 const string staticSymbolName = prefix ~ ToString!(id); 249 const string staticSymbolName = prefix ~ ToString!(id);
961 } 250 }
962 251
963 template signatureString(string name, A...) 252 template signatureString(string name, A...)
964 { 253 {
965 const string signatureString = name ~ "(" ~ joinArgs!(A) ~ ")"; 254 const string signatureString = name ~ "(" ~ joinArgs!(A) ~ ")";
966 } 255 }
967 256
257 // recursive search in the static meta-information
968 template findSymbolImpl(string prefix, C, int id, alias pred) 258 template findSymbolImpl(string prefix, C, int id, alias pred)
969 { 259 {
970 static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) ) 260 static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) )
971 { 261 {
972 mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;"); 262 mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;");
1004 static if (is(result == void)) 294 static if (is(result == void))
1005 static assert(0, "Signal " ~ name ~ " was not found."); 295 static assert(0, "Signal " ~ name ~ " was not found.");
1006 } 296 }
1007 } 297 }
1008 298
299 // recursive search in the static meta-information
300 template findSymbolsImpl(string prefix, C, int id, alias pred)
301 {
302 static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) )
303 {
304 mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;");
305 static if (pred!current) {
306 alias TupleWrapper!current subres;
307 // pragma(msg, toStringNow!id ~ " " ~ subres.stringof);
308 } else
309 alias TypeTuple!() subres;
310 alias TypeTuple!(subres, findSymbolsImpl!(prefix, C, id + 1, pred).result) result;
311 }
312 else
313 {
314 alias TypeTuple!() result;
315 }
316 }
317
318 template findSymbols(string prefix, C, alias pred)
319 {
320 alias findSymbolsImpl!(prefix, C, 0, pred).result findSymbols;
321 }
322
1009 string __toString(long v) 323 string __toString(long v)
1010 { 324 {
1011 if (v == 0) 325 if (v == 0)
1012 return "0"; 326 return "0";
1013 327
1030 ret = "-" ~ ret; 344 ret = "-" ~ ret;
1031 345
1032 return ret; 346 return ret;
1033 } 347 }
1034 348
1035 public string SignalEmitter(A...)(SignalType signalType, string name, int index) 349 string convertSignalArguments(Args...)()
350 {
351 // void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
352
353 string res = "void*[" ~ __toString(Args.length+1) ~ "] _a = [null";
354 foreach(i, _; Args)
355 res ~= ", " ~ "cast(void*) &" ~ convertSignalArgument!(Args[i])("_t" ~ __toString(i));
356 res ~= "];\n";
357 return res;
358 }
359
360 public string SignalEmitter(A...)(SignalType signalType, string name, string[] defVals, int localIndex)
1036 { 361 {
1037 string fullArgs, args; 362 string fullArgs, args;
363 int defValsLength = defVals.length;
364 string argsConversion = "";
365 string argsPtr = "null";
1038 static if (A.length) 366 static if (A.length)
1039 { 367 {
1040 fullArgs = A[0].stringof ~ " a0"; 368 while(A.length != defVals.length)
1041 args = ", a0"; 369 defVals = "" ~ defVals;
370
371 fullArgs = A[0].stringof ~ " _t0";
372 if (defVals[0].length)
373 fullArgs ~= " = " ~ defVals[0];
374 args = "_t0";
1042 foreach(i, _; A[1..$]) 375 foreach(i, _; A[1..$])
1043 { 376 {
1044 fullArgs ~= ", " ~ A[i+1].stringof ~ " a" ~ __toString(i+1); 377 fullArgs ~= ", " ~ A[i+1].stringof ~ " _t" ~ __toString(i+1);
1045 args ~= ", a" ~ __toString(i+1); 378 if (defVals[i+1].length)
379 fullArgs ~= " = " ~ defVals[i+1];
380 args ~= ", _t" ~ __toString(i+1);
1046 } 381 }
382 // build up conversion of signal args from D to C++
383 argsPtr = "_a.ptr";
384 argsConversion = convertSignalArguments!(A)();
1047 } 385 }
1048 string attribute; 386 string attribute;
1049 string sigName = name; 387 string sigName = name;
1050 if (signalType == SignalType.BindQtSignal) 388 if (signalType == SignalType.BindQtSignal)
1051 name ~= "_emit"; 389 name ~= "_emit";
1052 else 390 else
1053 attribute = "protected "; 391 attribute = "protected ";
1054 string str = attribute ~ "void " ~ name ~ "(" ~ fullArgs ~ ")" ~ 392
1055 "{ this.signalHandler.emit(" ~ __toString(index) ~ args ~ "); }"; 393 string indexArgs = __toString(localIndex);
394 if(defValsLength > 0)
395 indexArgs ~= ", " ~ __toString(localIndex+defValsLength);
396 string str = attribute ~ "final void " ~ name ~ "(" ~ fullArgs ~ ") {\n" ~ argsConversion ~ "\n"
397 ~ " QMetaObject.activate(this, typeof(this).staticMetaObject, " ~ indexArgs ~ ", " ~ argsPtr ~ ");\n"
398 ~ "}\n"; // ~
1056 return str; 399 return str;
1057 } 400 }
1058
1059 /** ---------------- */ 401 /** ---------------- */
1060 402
1061 403
1062 /** 404 const string signalPrefix = "__signal";
1063 Examples: 405 const string slotPrefix = "__slot";
1064 ---- 406
1065 struct Args 407 enum SignalType
1066 { 408 {
1067 bool cancel; 409 BindQtSignal,
1068 } 410 NewSignal,
1069 411 NewSlot
1070 class C 412 }
1071 { 413
1072 private int _x; 414 template BindQtSignal(string fullName)
1073 // reference parameters are not supported yet, 415 {
1074 // so we pass arguments by pointer. 416 mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.BindQtSignal);
1075 mixin Signal!("xChanging", int, Args*); 417 }
1076 mixin Signal!("xChanged"); 418
1077 419 template Signal(string fullName)
1078 void x(int v) 420 {
1079 { 421 mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.NewSignal);
1080 if (v != _x) 422 }
423
424 template Slot(string fullName)
425 {
426 mixin MetaMethodImpl!(slotPrefix, 0, fullName, SignalType.NewSlot);
427 }
428
429 template SignalImpl(int index, string fullName, SignalType signalType)
430 {
431 static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ signalPrefix ~ ToString!(index)))))
432 mixin SignalImpl!(index + 1, fullName, signalType);
433 else
434 {
435 // pragma(msg, "alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
436 mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
437 alias ParameterTypeTuple!(Dg) ArgTypes;
438 enum args = getFunc!_Args(fullName);
439 enum defVals = defaultValues(args);
440 enum defValsLength = defaultValuesLength(defVals);
441
442 // pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, index));
443 mixin InsertMetaSignal!(fullName, index, defValsLength, ArgTypes);
444 // pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength));
445 }
446 }
447 template MetaMethodImpl(string metaPrefix, int index, string fullName, SignalType signalType)
448 {
449 static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ metaPrefix ~ toStringNow!(index)))))
450 {
451 mixin MetaMethodImpl!(metaPrefix, index + 1, fullName, signalType);
452 }
453 else
454 {
455 mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
456 alias ParameterTypeTuple!(Dg) ArgTypes;
457 enum args = getFunc!_Args(fullName);
458 enum defVals = defaultValues(args);
459 enum defValsLength = defaultValuesLength(defVals);
460
461 static if (metaPrefix == signalPrefix)
1081 { 462 {
1082 Args args; 463 // calculating local index of the signal
1083 xChanging.emit(v, &args); 464 static if (typeof(this).stringof == "QObject")
1084 if (!args.cancel) 465 enum localIndex = index;
466 else
467 mixin ("enum localIndex = index - 1 - lastSignalIndex_" ~ (typeof(super)).stringof ~ ";");
468
469 static if (signalType == SignalType.NewSignal)
1085 { 470 {
1086 _x = v; 471 pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex));
1087 xChanged.emit; 472 mixin (SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex));
1088 } 473 }
1089 } 474 }
1090 } 475 mixin InsertMetaMethod!(fullName, metaPrefix, index, defValsLength, DefaultArgs.Start, ArgTypes);
1091 } 476 // pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength));
1092 ---- 477 }
1093 */ 478 }
1094 479 template InsertMetaMethod(string fullName, string metaPrefix, int index, int defValsCount, DefaultArgs defArgsInitFlag, ArgTypes...)
1095 enum SignalType 480 {
1096 { 481 // this identifies if metamethod is was generated by the presence of default args or not
1097 BindQtSignal, 482 static if(defValsCount > 0)
1098 NewSignal 483 {
1099 } 484 static if (defArgsInitFlag == DefaultArgs.Start)
1100 485 enum defValsFlag = DefaultArgs.Start;
1101 template BindQtSignal(string name, A...) 486 else
1102 { 487 enum defValsFlag = DefaultArgs.Continue;
1103 mixin SignalImpl!(0, name, SignalType.BindQtSignal, A); 488 }
1104 } 489 else
1105 490 {
1106 template Signal(string name, A...) 491 static if (defArgsInitFlag == DefaultArgs.Start)
1107 { 492 enum defValsFlag = DefaultArgs.None;
1108 mixin SignalImpl!(0, name, SignalType.NewSignal, A); 493 else
1109 } 494 enum defValsFlag = DefaultArgs.Continue;
1110 495 }
1111 template SignalImpl(int index, string name, SignalType signalType, A...) 496 static if(defValsCount >= 0)
1112 { 497 mixin("public alias TypeTuple!(\"" ~ getFunc!_Name(fullName) ~ "\", index, typeof(this), defValsFlag, ArgTypes) " ~ metaPrefix ~ toStringNow!(index) ~ ";");
1113 static if (is(typeof(mixin(typeof(this).stringof ~ ".__signal" ~ ToString!(index))))) 498 static if(defValsCount > 0)
1114 mixin SignalImpl!(index + 1, name, signalType, A); 499 mixin InsertMetaMethod!(fullName, metaPrefix, index+1, defValsCount-1, DefaultArgs.Continue, ArgTypes[0..$-1]);
1115 else 500 }
1116 { 501
1117 // mixed-in once 502
1118 static if (!is(typeof(this.signalHandler))) 503 string signature_impl(T...)(string name)
1119 mixin SignalHandlerOps; 504 {
1120 505 string res = name ~ "(";
1121 mixin (SignalEmitter!(A)(signalType, name, index)); 506 foreach(i, _; T)
1122 mixin("public alias TypeTuple!(\"" ~ name ~ "\", index, A) __signal" ~ ToString!(index) ~ ";"); 507 {
1123 } 508 if(i > 0)
1124 } 509 res ~= ",";
1125 510 res ~= T[i].stringof;
1126 extern(C) alias void function(void*) SlotConnector; 511 }
1127 512 res ~= ")";
1128 debug (UnitTest) 513 return res;
1129 { 514 }
1130 class A 515
1131 { 516 template signature(string name, T...)
1132 mixin Signal!("scorched", int); 517 {
1133 518 enum signature = signature_impl!(T)(name);
1134 int signalId1 = -1; 519 }
1135 int signalId2 = -1; 520
1136 521 template lastSignalIndex(T)
1137 void onFirstConnect(int sId) 522 {
1138 { 523 static if (T.stringof == "QObject")
1139 signalId1 = sId; 524 enum lastSignalIndex = lastSignalIndexImpl!(T, 0);
1140 } 525 else
1141 526 mixin ("enum lastSignalIndex = lastSignalIndexImpl!(T, " ~ "T.lastSignalIndex_" ~ (BaseClassesTuple!(T)[0]).stringof ~ ");");
1142 void onLastDisconnect(int sId) 527 }
1143 { 528
1144 signalId2 = sId; 529 template lastSignalIndexImpl(T, int index)
1145 } 530 {
1146 531 static if (is(typeof(mixin("T." ~ signalPrefix ~ toStringNow!(index)))))
1147 this() 532 enum lastSignalIndexImpl = lastSignalIndexImpl!(T, index + 1);
1148 { 533 else
1149 signalHandler.firstSlotConnected = &onFirstConnect; 534 enum lastSignalIndexImpl = index - 1;
1150 signalHandler.lastSlotDisconnected = &onLastDisconnect; 535 }
1151 }
1152 }
1153
1154 class B : A
1155 {
1156 mixin Signal!("booed", int);
1157
1158 int bazSum;
1159 void baz(int i)
1160 {
1161 bazSum += i;
1162 }
1163 }
1164
1165 class C : A
1166 {
1167 mixin Signal!("cooked");
1168 }
1169 }
1170
1171 unittest
1172 {
1173 static int fooSum;
1174 static int barSum;
1175
1176 static void foo(int i)
1177 {
1178 fooSum += i;
1179 }
1180
1181 void bar(long i)
1182 {
1183 barSum += i;
1184 }
1185
1186 auto a = new A;
1187 auto b = new B;
1188 auto c = new C;
1189 assert(b.scorched.signalId == 0);
1190 assert(b.booed.signalId == 1);
1191 assert(c.cooked.signalId == 1);
1192
1193 auto sh = b.signalHandler;
1194
1195 b.scorched.connect(&foo);
1196 assert(sh.connections.length == 1);
1197 assert(b.signalId1 == 0);
1198 auto scCons = &sh.connections[0];
1199
1200 assert(scCons.getSlotList!(SlotListId.Func).length == 1);
1201 b.scorched.emit(1);
1202 assert(fooSum == 1);
1203
1204 b.scorched.connect(&bar, ConnectionFlags.NoObject);
1205 assert(sh.connections.length == 1);
1206 assert(scCons.getSlotList!(SlotListId.Strong).length == 1);
1207 b.scorched.emit(1);
1208 assert (fooSum == 2 && barSum == 1);
1209
1210 b.scorched.connect(&b.baz);
1211 assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
1212 b.scorched.emit(1);
1213 assert (fooSum == 3 && barSum == 2 && b.bazSum == 1);
1214
1215 b.scorched.disconnect(&bar);
1216 assert(scCons.slotCount == 2);
1217 b.scorched.disconnect(&b.baz);
1218 assert(scCons.slotCount == 1);
1219 b.scorched.disconnect(&foo);
1220 assert(scCons.slotCount == 0);
1221 assert(b.signalId2 == 0);
1222
1223 fooSum = 0;
1224 void connectFoo()
1225 {
1226 b.scorched.connect(&foo);
1227 b.scorched.disconnect(&connectFoo);
1228 }
1229
1230 b.scorched.connect(&connectFoo, ConnectionFlags.NoObject);
1231 b.scorched.emit(1);
1232 assert(scCons.getSlotList!(SlotListId.Func).length == 1);
1233 assert(scCons.getSlotList!(SlotListId.Strong).length == 0);
1234 assert(!fooSum);
1235
1236 auto r = new B();
1237 b.scorched.connect(&r.baz);
1238 assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
1239 b.scorched.emit(1);
1240 assert(r.bazSum == 1);
1241 assert(fooSum == 1);
1242
1243 delete(r);
1244 assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
1245 b.scorched.emit(1);
1246 assert(scCons.getSlotList!(SlotListId.Weak).length == 0);
1247 }