xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/src/std/signals.d (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1*627f7eb2Smrg // Written in the D programming language.
2*627f7eb2Smrg 
3*627f7eb2Smrg /**
4*627f7eb2Smrg  * Signals and Slots are an implementation of the Observer Pattern.
5*627f7eb2Smrg  * Essentially, when a Signal is emitted, a list of connected Observers
6*627f7eb2Smrg  * (called slots) are called.
7*627f7eb2Smrg  *
8*627f7eb2Smrg  * There have been several D implementations of Signals and Slots.
9*627f7eb2Smrg  * This version makes use of several new features in D, which make
10*627f7eb2Smrg  * using it simpler and less error prone. In particular, it is no
11*627f7eb2Smrg  * longer necessary to instrument the slots.
12*627f7eb2Smrg  *
13*627f7eb2Smrg  * References:
14*627f7eb2Smrg  *      $(LUCKY A Deeper Look at Signals and Slots)$(BR)
15*627f7eb2Smrg  *      $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR)
16*627f7eb2Smrg  *      $(LINK2 http://en.wikipedia.org/wiki/Signals_and_slots, Wikipedia)$(BR)
17*627f7eb2Smrg  *      $(LINK2 http://boost.org/doc/html/$(SIGNALS).html, Boost Signals)$(BR)
18*627f7eb2Smrg  *      $(LINK2 http://qt-project.org/doc/qt-5/signalsandslots.html, Qt)$(BR)
19*627f7eb2Smrg  *
20*627f7eb2Smrg  *      There has been a great deal of discussion in the D newsgroups
21*627f7eb2Smrg  *      over this, and several implementations:
22*627f7eb2Smrg  *
23*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/signal_slots_library_4825.html, signal slots library)$(BR)
24*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Signals_and_Slots_in_D_42387.html, Signals and Slots in D)$(BR)
25*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dynamic_binding_--_Qt_s_Signals_and_Slots_vs_Objective-C_42260.html, Dynamic binding -- Qt's Signals and Slots vs Objective-C)$(BR)
26*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dissecting_the_SS_42377.html, Dissecting the SS)$(BR)
27*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/dwt/about_harmonia_454.html, about harmonia)$(BR)
28*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/1502.html, Another event handling module)$(BR)
29*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/41825.html, Suggestion: signal/slot mechanism)$(BR)
30*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/13251.html, Signals and slots?)$(BR)
31*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/10714.html, Signals and slots ready for evaluation)$(BR)
32*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/1393.html, Signals & Slots for Walter)$(BR)
33*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/28456.html, Signal/Slot mechanism?)$(BR)
34*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/19470.html, Modern Features?)$(BR)
35*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/16592.html, Delegates vs interfaces)$(BR)
36*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/16583.html, The importance of component programming (properties$(COMMA) signals and slots$(COMMA) etc))$(BR)
37*627f7eb2Smrg  *      $(LINK2 http://www.digitalmars.com/d/archives/16368.html, signals and slots)$(BR)
38*627f7eb2Smrg  *
39*627f7eb2Smrg  * Bugs:
40*627f7eb2Smrg  *      Slots can only be delegates formed from class objects or
41*627f7eb2Smrg  *      interfaces to class objects. If a delegate to something else
42*627f7eb2Smrg  *      is passed to connect(), such as a struct member function,
43*627f7eb2Smrg  *      a nested function or a COM interface, undefined behavior
44*627f7eb2Smrg  *      will result.
45*627f7eb2Smrg  *
46*627f7eb2Smrg  *      Not safe for multiple threads operating on the same signals
47*627f7eb2Smrg  *      or slots.
48*627f7eb2Smrg  * Macros:
49*627f7eb2Smrg  *      SIGNALS=signals
50*627f7eb2Smrg  *
51*627f7eb2Smrg  * Copyright: Copyright Digital Mars 2000 - 2009.
52*627f7eb2Smrg  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
53*627f7eb2Smrg  * Authors:   $(HTTP digitalmars.com, Walter Bright)
54*627f7eb2Smrg  * Source:    $(PHOBOSSRC std/_signals.d)
55*627f7eb2Smrg  *
56*627f7eb2Smrg  * $(SCRIPT inhibitQuickIndex = 1;)
57*627f7eb2Smrg  */
58*627f7eb2Smrg /*          Copyright Digital Mars 2000 - 2009.
59*627f7eb2Smrg  * Distributed under the Boost Software License, Version 1.0.
60*627f7eb2Smrg  *    (See accompanying file LICENSE_1_0.txt or copy at
61*627f7eb2Smrg  *          http://www.boost.org/LICENSE_1_0.txt)
62*627f7eb2Smrg  */
63*627f7eb2Smrg module std.signals;
64*627f7eb2Smrg 
65*627f7eb2Smrg import core.exception : onOutOfMemoryError;
66*627f7eb2Smrg import core.stdc.stdlib : calloc, realloc, free;
67*627f7eb2Smrg import std.stdio;
68*627f7eb2Smrg 
69*627f7eb2Smrg // Special function for internal use only.
70*627f7eb2Smrg // Use of this is where the slot had better be a delegate
71*627f7eb2Smrg // to an object or an interface that is part of an object.
72*627f7eb2Smrg extern (C) Object _d_toObject(void* p);
73*627f7eb2Smrg 
74*627f7eb2Smrg // Used in place of Object.notifyRegister and Object.notifyUnRegister.
75*627f7eb2Smrg alias DisposeEvt = void delegate(Object);
76*627f7eb2Smrg extern (C) void  rt_attachDisposeEvent( Object obj, DisposeEvt evt );
77*627f7eb2Smrg extern (C) void  rt_detachDisposeEvent( Object obj, DisposeEvt evt );
78*627f7eb2Smrg //debug=signal;
79*627f7eb2Smrg 
80*627f7eb2Smrg /************************
81*627f7eb2Smrg  * Mixin to create a signal within a class object.
82*627f7eb2Smrg  *
83*627f7eb2Smrg  * Different signals can be added to a class by naming the mixins.
84*627f7eb2Smrg  */
85*627f7eb2Smrg 
Signal(T1...)86*627f7eb2Smrg mixin template Signal(T1...)
87*627f7eb2Smrg {
88*627f7eb2Smrg     static import core.exception;
89*627f7eb2Smrg     static import core.stdc.stdlib;
90*627f7eb2Smrg     /***
91*627f7eb2Smrg      * A slot is implemented as a delegate.
92*627f7eb2Smrg      * The slot_t is the type of the delegate.
93*627f7eb2Smrg      * The delegate must be to an instance of a class or an interface
94*627f7eb2Smrg      * to a class instance.
95*627f7eb2Smrg      * Delegates to struct instances or nested functions must not be
96*627f7eb2Smrg      * used as slots.
97*627f7eb2Smrg      */
98*627f7eb2Smrg     alias slot_t = void delegate(T1);
99*627f7eb2Smrg 
100*627f7eb2Smrg     /***
101*627f7eb2Smrg      * Call each of the connected slots, passing the argument(s) i to them.
102*627f7eb2Smrg      * Nested call will be ignored.
103*627f7eb2Smrg      */
104*627f7eb2Smrg     final void emit( T1 i )
105*627f7eb2Smrg     {
106*627f7eb2Smrg         if (status >= ST.inemitting || !slots.length)
107*627f7eb2Smrg             return; // should not nest
108*627f7eb2Smrg 
109*627f7eb2Smrg         status = ST.inemitting;
110*627f7eb2Smrg         scope (exit)
111*627f7eb2Smrg             status = ST.idle;
112*627f7eb2Smrg 
113*627f7eb2Smrg         foreach (slot; slots[0 .. slots_idx])
114*627f7eb2Smrg         {   if (slot)
115*627f7eb2Smrg                 slot(i);
116*627f7eb2Smrg         }
117*627f7eb2Smrg 
118*627f7eb2Smrg         assert(status >= ST.inemitting);
119*627f7eb2Smrg         if (status == ST.inemitting_disconnected)
120*627f7eb2Smrg         {
121*627f7eb2Smrg             for (size_t j = 0; j < slots_idx;)
122*627f7eb2Smrg             {
123*627f7eb2Smrg                 if (slots[j] is null)
124*627f7eb2Smrg                 {
125*627f7eb2Smrg                     slots_idx--;
126*627f7eb2Smrg                     slots[j] = slots[slots_idx];
127*627f7eb2Smrg                 }
128*627f7eb2Smrg                 else
129*627f7eb2Smrg                     j++;
130*627f7eb2Smrg             }
131*627f7eb2Smrg         }
132*627f7eb2Smrg     }
133*627f7eb2Smrg 
134*627f7eb2Smrg     /***
135*627f7eb2Smrg      * Add a slot to the list of slots to be called when emit() is called.
136*627f7eb2Smrg      */
137*627f7eb2Smrg     final void connect(slot_t slot)
138*627f7eb2Smrg     {
139*627f7eb2Smrg         /* Do this:
140*627f7eb2Smrg          *    slots ~= slot;
141*627f7eb2Smrg          * but use malloc() and friends instead
142*627f7eb2Smrg          */
143*627f7eb2Smrg         auto len = slots.length;
144*627f7eb2Smrg         if (slots_idx == len)
145*627f7eb2Smrg         {
146*627f7eb2Smrg             if (slots.length == 0)
147*627f7eb2Smrg             {
148*627f7eb2Smrg                 len = 4;
149*627f7eb2Smrg                 auto p = core.stdc.stdlib.calloc(slot_t.sizeof, len);
150*627f7eb2Smrg                 if (!p)
151*627f7eb2Smrg                     core.exception.onOutOfMemoryError();
152*627f7eb2Smrg                 slots = (cast(slot_t*) p)[0 .. len];
153*627f7eb2Smrg             }
154*627f7eb2Smrg             else
155*627f7eb2Smrg             {
156*627f7eb2Smrg                 import core.checkedint : addu, mulu;
157*627f7eb2Smrg                 bool overflow;
158*627f7eb2Smrg                 len = addu(mulu(len, 2, overflow), 4, overflow); // len = len * 2 + 4
159*627f7eb2Smrg                 const nbytes = mulu(len, slot_t.sizeof, overflow);
160*627f7eb2Smrg                 if (overflow) assert(0);
161*627f7eb2Smrg 
162*627f7eb2Smrg                 auto p = core.stdc.stdlib.realloc(slots.ptr, nbytes);
163*627f7eb2Smrg                 if (!p)
164*627f7eb2Smrg                     core.exception.onOutOfMemoryError();
165*627f7eb2Smrg                 slots = (cast(slot_t*) p)[0 .. len];
166*627f7eb2Smrg                 slots[slots_idx + 1 .. $] = null;
167*627f7eb2Smrg             }
168*627f7eb2Smrg         }
169*627f7eb2Smrg         slots[slots_idx++] = slot;
170*627f7eb2Smrg 
171*627f7eb2Smrg      L1:
172*627f7eb2Smrg         Object o = _d_toObject(slot.ptr);
173*627f7eb2Smrg         rt_attachDisposeEvent(o, &unhook);
174*627f7eb2Smrg     }
175*627f7eb2Smrg 
176*627f7eb2Smrg     /***
177*627f7eb2Smrg      * Remove a slot from the list of slots to be called when emit() is called.
178*627f7eb2Smrg      */
179*627f7eb2Smrg     final void disconnect(slot_t slot)
180*627f7eb2Smrg     {
181*627f7eb2Smrg         debug (signal) writefln("Signal.disconnect(slot)");
182*627f7eb2Smrg         size_t disconnectedSlots = 0;
183*627f7eb2Smrg         size_t instancePreviousSlots = 0;
184*627f7eb2Smrg         if (status >= ST.inemitting)
185*627f7eb2Smrg         {
186*627f7eb2Smrg             foreach (i, sloti; slots[0 .. slots_idx])
187*627f7eb2Smrg             {
188*627f7eb2Smrg                 if (sloti.ptr == slot.ptr &&
189*627f7eb2Smrg                     ++instancePreviousSlots &&
190*627f7eb2Smrg                     sloti == slot)
191*627f7eb2Smrg                 {
192*627f7eb2Smrg                     disconnectedSlots++;
193*627f7eb2Smrg                     slots[i] = null;
194*627f7eb2Smrg                     status = ST.inemitting_disconnected;
195*627f7eb2Smrg                 }
196*627f7eb2Smrg             }
197*627f7eb2Smrg         }
198*627f7eb2Smrg         else
199*627f7eb2Smrg         {
200*627f7eb2Smrg             for (size_t i = 0; i < slots_idx; )
201*627f7eb2Smrg             {
202*627f7eb2Smrg                 if (slots[i].ptr == slot.ptr &&
203*627f7eb2Smrg                     ++instancePreviousSlots &&
204*627f7eb2Smrg                     slots[i] == slot)
205*627f7eb2Smrg                 {
206*627f7eb2Smrg                     slots_idx--;
207*627f7eb2Smrg                     disconnectedSlots++;
208*627f7eb2Smrg                     slots[i] = slots[slots_idx];
209*627f7eb2Smrg                     slots[slots_idx] = null;        // not strictly necessary
210*627f7eb2Smrg                 }
211*627f7eb2Smrg                 else
212*627f7eb2Smrg                     i++;
213*627f7eb2Smrg             }
214*627f7eb2Smrg         }
215*627f7eb2Smrg 
216*627f7eb2Smrg          // detach object from dispose event if all its slots have been removed
217*627f7eb2Smrg         if (instancePreviousSlots == disconnectedSlots)
218*627f7eb2Smrg         {
219*627f7eb2Smrg             Object o = _d_toObject(slot.ptr);
220*627f7eb2Smrg             rt_detachDisposeEvent(o, &unhook);
221*627f7eb2Smrg         }
222*627f7eb2Smrg      }
223*627f7eb2Smrg 
224*627f7eb2Smrg     /* **
225*627f7eb2Smrg      * Special function called when o is destroyed.
226*627f7eb2Smrg      * It causes any slots dependent on o to be removed from the list
227*627f7eb2Smrg      * of slots to be called by emit().
228*627f7eb2Smrg      */
229*627f7eb2Smrg     final void unhook(Object o)
230*627f7eb2Smrg     in { assert( status == ST.idle ); }
231*627f7eb2Smrg     body {
232*627f7eb2Smrg         debug (signal) writefln("Signal.unhook(o = %s)", cast(void*) o);
233*627f7eb2Smrg         for (size_t i = 0; i < slots_idx; )
234*627f7eb2Smrg         {
235*627f7eb2Smrg             if (_d_toObject(slots[i].ptr) is o)
236*627f7eb2Smrg             {   slots_idx--;
237*627f7eb2Smrg                 slots[i] = slots[slots_idx];
238*627f7eb2Smrg                 slots[slots_idx] = null;        // not strictly necessary
239*627f7eb2Smrg             }
240*627f7eb2Smrg             else
241*627f7eb2Smrg                 i++;
242*627f7eb2Smrg         }
243*627f7eb2Smrg     }
244*627f7eb2Smrg 
245*627f7eb2Smrg     /* **
246*627f7eb2Smrg      * There can be multiple destructors inserted by mixins.
247*627f7eb2Smrg      */
248*627f7eb2Smrg     ~this()
249*627f7eb2Smrg     {
250*627f7eb2Smrg         /* **
251*627f7eb2Smrg          * When this object is destroyed, need to let every slot
252*627f7eb2Smrg          * know that this object is destroyed so they are not left
253*627f7eb2Smrg          * with dangling references to it.
254*627f7eb2Smrg          */
255*627f7eb2Smrg         if (slots.length)
256*627f7eb2Smrg         {
257*627f7eb2Smrg             foreach (slot; slots[0 .. slots_idx])
258*627f7eb2Smrg             {
259*627f7eb2Smrg                 if (slot)
260*627f7eb2Smrg                 {   Object o = _d_toObject(slot.ptr);
261*627f7eb2Smrg                     rt_detachDisposeEvent(o, &unhook);
262*627f7eb2Smrg                 }
263*627f7eb2Smrg             }
264*627f7eb2Smrg             core.stdc.stdlib.free(slots.ptr);
265*627f7eb2Smrg             slots = null;
266*627f7eb2Smrg         }
267*627f7eb2Smrg     }
268*627f7eb2Smrg 
269*627f7eb2Smrg   private:
270*627f7eb2Smrg     slot_t[] slots;             // the slots to call from emit()
271*627f7eb2Smrg     size_t slots_idx;           // used length of slots[]
272*627f7eb2Smrg 
273*627f7eb2Smrg     enum ST { idle, inemitting, inemitting_disconnected }
274*627f7eb2Smrg     ST status;
275*627f7eb2Smrg }
276*627f7eb2Smrg 
277*627f7eb2Smrg ///
278*627f7eb2Smrg @system unittest
279*627f7eb2Smrg {
280*627f7eb2Smrg     import std.signals;
281*627f7eb2Smrg 
282*627f7eb2Smrg     int observedMessageCounter = 0;
283*627f7eb2Smrg 
284*627f7eb2Smrg     class Observer
285*627f7eb2Smrg     {   // our slot
watch(string msg,int value)286*627f7eb2Smrg         void watch(string msg, int value)
287*627f7eb2Smrg         {
288*627f7eb2Smrg             switch (observedMessageCounter++)
289*627f7eb2Smrg             {
290*627f7eb2Smrg                 case 0:
291*627f7eb2Smrg                     assert(msg == "setting new value");
292*627f7eb2Smrg                     assert(value == 4);
293*627f7eb2Smrg                     break;
294*627f7eb2Smrg                 case 1:
295*627f7eb2Smrg                     assert(msg == "setting new value");
296*627f7eb2Smrg                     assert(value == 6);
297*627f7eb2Smrg                     break;
298*627f7eb2Smrg                 default:
299*627f7eb2Smrg                     assert(0, "Unknown observation");
300*627f7eb2Smrg             }
301*627f7eb2Smrg         }
302*627f7eb2Smrg     }
303*627f7eb2Smrg 
304*627f7eb2Smrg     class Observer2
305*627f7eb2Smrg     {   // our slot
watch(string msg,int value)306*627f7eb2Smrg         void watch(string msg, int value)
307*627f7eb2Smrg         {
308*627f7eb2Smrg         }
309*627f7eb2Smrg     }
310*627f7eb2Smrg 
311*627f7eb2Smrg     class Foo
312*627f7eb2Smrg     {
value()313*627f7eb2Smrg         int value() { return _value; }
314*627f7eb2Smrg 
value(int v)315*627f7eb2Smrg         int value(int v)
316*627f7eb2Smrg         {
317*627f7eb2Smrg             if (v != _value)
318*627f7eb2Smrg             {   _value = v;
319*627f7eb2Smrg                 // call all the connected slots with the two parameters
320*627f7eb2Smrg                 emit("setting new value", v);
321*627f7eb2Smrg             }
322*627f7eb2Smrg             return v;
323*627f7eb2Smrg         }
324*627f7eb2Smrg 
325*627f7eb2Smrg         // Mix in all the code we need to make Foo into a signal
326*627f7eb2Smrg         mixin Signal!(string, int);
327*627f7eb2Smrg 
328*627f7eb2Smrg       private :
329*627f7eb2Smrg         int _value;
330*627f7eb2Smrg     }
331*627f7eb2Smrg 
332*627f7eb2Smrg     Foo a = new Foo;
333*627f7eb2Smrg     Observer o = new Observer;
334*627f7eb2Smrg     auto o2 = new Observer2;
335*627f7eb2Smrg     auto o3 = new Observer2;
336*627f7eb2Smrg     auto o4 = new Observer2;
337*627f7eb2Smrg     auto o5 = new Observer2;
338*627f7eb2Smrg 
339*627f7eb2Smrg     a.value = 3;                // should not call o.watch()
340*627f7eb2Smrg     a.connect(&o.watch);        // o.watch is the slot
341*627f7eb2Smrg     a.connect(&o2.watch);
342*627f7eb2Smrg     a.connect(&o3.watch);
343*627f7eb2Smrg     a.connect(&o4.watch);
344*627f7eb2Smrg     a.connect(&o5.watch);
345*627f7eb2Smrg     a.value = 4;                // should call o.watch()
346*627f7eb2Smrg     a.disconnect(&o.watch);     // o.watch is no longer a slot
347*627f7eb2Smrg     a.disconnect(&o3.watch);
348*627f7eb2Smrg     a.disconnect(&o5.watch);
349*627f7eb2Smrg     a.disconnect(&o4.watch);
350*627f7eb2Smrg     a.disconnect(&o2.watch);
351*627f7eb2Smrg     a.value = 5;                // so should not call o.watch()
352*627f7eb2Smrg     a.connect(&o2.watch);
353*627f7eb2Smrg     a.connect(&o.watch);        // connect again
354*627f7eb2Smrg     a.value = 6;                // should call o.watch()
355*627f7eb2Smrg     destroy(o);                 // destroying o should automatically disconnect it
356*627f7eb2Smrg     a.value = 7;                // should not call o.watch()
357*627f7eb2Smrg 
358*627f7eb2Smrg     assert(observedMessageCounter == 2);
359*627f7eb2Smrg }
360*627f7eb2Smrg 
361*627f7eb2Smrg // A function whose sole purpose is to get this module linked in
362*627f7eb2Smrg // so the unittest will run.
linkin()363*627f7eb2Smrg void linkin() { }
364*627f7eb2Smrg 
365*627f7eb2Smrg @system unittest
366*627f7eb2Smrg {
367*627f7eb2Smrg     class Observer
368*627f7eb2Smrg     {
watch(string msg,int i)369*627f7eb2Smrg         void watch(string msg, int i)
370*627f7eb2Smrg         {
371*627f7eb2Smrg             //writefln("Observed msg '%s' and value %s", msg, i);
372*627f7eb2Smrg             captured_value = i;
373*627f7eb2Smrg             captured_msg   = msg;
374*627f7eb2Smrg         }
375*627f7eb2Smrg 
376*627f7eb2Smrg         int    captured_value;
377*627f7eb2Smrg         string captured_msg;
378*627f7eb2Smrg     }
379*627f7eb2Smrg 
380*627f7eb2Smrg     class Foo
381*627f7eb2Smrg     {
value()382*627f7eb2Smrg         @property int value() { return _value; }
383*627f7eb2Smrg 
value(int v)384*627f7eb2Smrg         @property int value(int v)
385*627f7eb2Smrg         {
386*627f7eb2Smrg             if (v != _value)
387*627f7eb2Smrg             {   _value = v;
388*627f7eb2Smrg                 emit("setting new value", v);
389*627f7eb2Smrg             }
390*627f7eb2Smrg             return v;
391*627f7eb2Smrg         }
392*627f7eb2Smrg 
393*627f7eb2Smrg         mixin Signal!(string, int);
394*627f7eb2Smrg 
395*627f7eb2Smrg       private:
396*627f7eb2Smrg         int _value;
397*627f7eb2Smrg     }
398*627f7eb2Smrg 
399*627f7eb2Smrg     Foo a = new Foo;
400*627f7eb2Smrg     Observer o = new Observer;
401*627f7eb2Smrg 
402*627f7eb2Smrg     // check initial condition
403*627f7eb2Smrg     assert(o.captured_value == 0);
404*627f7eb2Smrg     assert(o.captured_msg == "");
405*627f7eb2Smrg 
406*627f7eb2Smrg     // set a value while no observation is in place
407*627f7eb2Smrg     a.value = 3;
408*627f7eb2Smrg     assert(o.captured_value == 0);
409*627f7eb2Smrg     assert(o.captured_msg == "");
410*627f7eb2Smrg 
411*627f7eb2Smrg     // connect the watcher and trigger it
412*627f7eb2Smrg     a.connect(&o.watch);
413*627f7eb2Smrg     a.value = 4;
414*627f7eb2Smrg     assert(o.captured_value == 4);
415*627f7eb2Smrg     assert(o.captured_msg == "setting new value");
416*627f7eb2Smrg 
417*627f7eb2Smrg     // disconnect the watcher and make sure it doesn't trigger
418*627f7eb2Smrg     a.disconnect(&o.watch);
419*627f7eb2Smrg     a.value = 5;
420*627f7eb2Smrg     assert(o.captured_value == 4);
421*627f7eb2Smrg     assert(o.captured_msg == "setting new value");
422*627f7eb2Smrg 
423*627f7eb2Smrg     // reconnect the watcher and make sure it triggers
424*627f7eb2Smrg     a.connect(&o.watch);
425*627f7eb2Smrg     a.value = 6;
426*627f7eb2Smrg     assert(o.captured_value == 6);
427*627f7eb2Smrg     assert(o.captured_msg == "setting new value");
428*627f7eb2Smrg 
429*627f7eb2Smrg     // destroy the underlying object and make sure it doesn't cause
430*627f7eb2Smrg     // a crash or other problems
431*627f7eb2Smrg     destroy(o);
432*627f7eb2Smrg     a.value = 7;
433*627f7eb2Smrg }
434*627f7eb2Smrg 
435*627f7eb2Smrg @system unittest
436*627f7eb2Smrg {
437*627f7eb2Smrg     class Observer
438*627f7eb2Smrg     {
439*627f7eb2Smrg         int    i;
440*627f7eb2Smrg         long   l;
441*627f7eb2Smrg         string str;
442*627f7eb2Smrg 
watchInt(string str,int i)443*627f7eb2Smrg         void watchInt(string str, int i)
444*627f7eb2Smrg         {
445*627f7eb2Smrg             this.str = str;
446*627f7eb2Smrg             this.i = i;
447*627f7eb2Smrg         }
448*627f7eb2Smrg 
watchLong(string str,long l)449*627f7eb2Smrg         void watchLong(string str, long l)
450*627f7eb2Smrg         {
451*627f7eb2Smrg             this.str = str;
452*627f7eb2Smrg             this.l = l;
453*627f7eb2Smrg         }
454*627f7eb2Smrg     }
455*627f7eb2Smrg 
456*627f7eb2Smrg     class Bar
457*627f7eb2Smrg     {
value1(int v)458*627f7eb2Smrg         @property void value1(int v)  { s1.emit("str1", v); }
value2(int v)459*627f7eb2Smrg         @property void value2(int v)  { s2.emit("str2", v); }
value3(long v)460*627f7eb2Smrg         @property void value3(long v) { s3.emit("str3", v); }
461*627f7eb2Smrg 
462*627f7eb2Smrg         mixin Signal!(string, int)  s1;
463*627f7eb2Smrg         mixin Signal!(string, int)  s2;
464*627f7eb2Smrg         mixin Signal!(string, long) s3;
465*627f7eb2Smrg     }
466*627f7eb2Smrg 
test(T)467*627f7eb2Smrg     void test(T)(T a) {
468*627f7eb2Smrg         auto o1 = new Observer;
469*627f7eb2Smrg         auto o2 = new Observer;
470*627f7eb2Smrg         auto o3 = new Observer;
471*627f7eb2Smrg 
472*627f7eb2Smrg         // connect the watcher and trigger it
473*627f7eb2Smrg         a.s1.connect(&o1.watchInt);
474*627f7eb2Smrg         a.s2.connect(&o2.watchInt);
475*627f7eb2Smrg         a.s3.connect(&o3.watchLong);
476*627f7eb2Smrg 
477*627f7eb2Smrg         assert(!o1.i && !o1.l && o1.str == null);
478*627f7eb2Smrg         assert(!o2.i && !o2.l && o2.str == null);
479*627f7eb2Smrg         assert(!o3.i && !o3.l && o3.str == null);
480*627f7eb2Smrg 
481*627f7eb2Smrg         a.value1 = 11;
482*627f7eb2Smrg         assert(o1.i == 11 && !o1.l && o1.str == "str1");
483*627f7eb2Smrg         assert(!o2.i && !o2.l && o2.str == null);
484*627f7eb2Smrg         assert(!o3.i && !o3.l && o3.str == null);
485*627f7eb2Smrg         o1.i = -11; o1.str = "x1";
486*627f7eb2Smrg 
487*627f7eb2Smrg         a.value2 = 12;
488*627f7eb2Smrg         assert(o1.i == -11 && !o1.l && o1.str == "x1");
489*627f7eb2Smrg         assert(o2.i == 12 && !o2.l && o2.str == "str2");
490*627f7eb2Smrg         assert(!o3.i && !o3.l && o3.str == null);
491*627f7eb2Smrg         o2.i = -12; o2.str = "x2";
492*627f7eb2Smrg 
493*627f7eb2Smrg         a.value3 = 13;
494*627f7eb2Smrg         assert(o1.i == -11 && !o1.l && o1.str == "x1");
495*627f7eb2Smrg         assert(o2.i == -12 && !o1.l && o2.str == "x2");
496*627f7eb2Smrg         assert(!o3.i && o3.l == 13 && o3.str == "str3");
497*627f7eb2Smrg         o3.l = -13; o3.str = "x3";
498*627f7eb2Smrg 
499*627f7eb2Smrg         // disconnect the watchers and make sure it doesn't trigger
500*627f7eb2Smrg         a.s1.disconnect(&o1.watchInt);
501*627f7eb2Smrg         a.s2.disconnect(&o2.watchInt);
502*627f7eb2Smrg         a.s3.disconnect(&o3.watchLong);
503*627f7eb2Smrg 
504*627f7eb2Smrg         a.value1 = 21;
505*627f7eb2Smrg         a.value2 = 22;
506*627f7eb2Smrg         a.value3 = 23;
507*627f7eb2Smrg         assert(o1.i == -11 && !o1.l && o1.str == "x1");
508*627f7eb2Smrg         assert(o2.i == -12 && !o1.l && o2.str == "x2");
509*627f7eb2Smrg         assert(!o3.i && o3.l == -13 && o3.str == "x3");
510*627f7eb2Smrg 
511*627f7eb2Smrg         // reconnect the watcher and make sure it triggers
512*627f7eb2Smrg         a.s1.connect(&o1.watchInt);
513*627f7eb2Smrg         a.s2.connect(&o2.watchInt);
514*627f7eb2Smrg         a.s3.connect(&o3.watchLong);
515*627f7eb2Smrg 
516*627f7eb2Smrg         a.value1 = 31;
517*627f7eb2Smrg         a.value2 = 32;
518*627f7eb2Smrg         a.value3 = 33;
519*627f7eb2Smrg         assert(o1.i == 31 && !o1.l && o1.str == "str1");
520*627f7eb2Smrg         assert(o2.i == 32 && !o1.l && o2.str == "str2");
521*627f7eb2Smrg         assert(!o3.i && o3.l == 33 && o3.str == "str3");
522*627f7eb2Smrg 
523*627f7eb2Smrg         // destroy observers
524*627f7eb2Smrg         destroy(o1);
525*627f7eb2Smrg         destroy(o2);
526*627f7eb2Smrg         destroy(o3);
527*627f7eb2Smrg         a.value1 = 41;
528*627f7eb2Smrg         a.value2 = 42;
529*627f7eb2Smrg         a.value3 = 43;
530*627f7eb2Smrg     }
531*627f7eb2Smrg 
532*627f7eb2Smrg     test(new Bar);
533*627f7eb2Smrg 
534*627f7eb2Smrg     class BarDerived: Bar
535*627f7eb2Smrg     {
value4(int v)536*627f7eb2Smrg         @property void value4(int v)  { s4.emit("str4", v); }
value5(int v)537*627f7eb2Smrg         @property void value5(int v)  { s5.emit("str5", v); }
value6(long v)538*627f7eb2Smrg         @property void value6(long v) { s6.emit("str6", v); }
539*627f7eb2Smrg 
540*627f7eb2Smrg         mixin Signal!(string, int)  s4;
541*627f7eb2Smrg         mixin Signal!(string, int)  s5;
542*627f7eb2Smrg         mixin Signal!(string, long) s6;
543*627f7eb2Smrg     }
544*627f7eb2Smrg 
545*627f7eb2Smrg     auto a = new BarDerived;
546*627f7eb2Smrg 
547*627f7eb2Smrg     test!Bar(a);
548*627f7eb2Smrg     test!BarDerived(a);
549*627f7eb2Smrg 
550*627f7eb2Smrg     auto o4 = new Observer;
551*627f7eb2Smrg     auto o5 = new Observer;
552*627f7eb2Smrg     auto o6 = new Observer;
553*627f7eb2Smrg 
554*627f7eb2Smrg     // connect the watcher and trigger it
555*627f7eb2Smrg     a.s4.connect(&o4.watchInt);
556*627f7eb2Smrg     a.s5.connect(&o5.watchInt);
557*627f7eb2Smrg     a.s6.connect(&o6.watchLong);
558*627f7eb2Smrg 
559*627f7eb2Smrg     assert(!o4.i && !o4.l && o4.str == null);
560*627f7eb2Smrg     assert(!o5.i && !o5.l && o5.str == null);
561*627f7eb2Smrg     assert(!o6.i && !o6.l && o6.str == null);
562*627f7eb2Smrg 
563*627f7eb2Smrg     a.value4 = 44;
564*627f7eb2Smrg     assert(o4.i == 44 && !o4.l && o4.str == "str4");
565*627f7eb2Smrg     assert(!o5.i && !o5.l && o5.str == null);
566*627f7eb2Smrg     assert(!o6.i && !o6.l && o6.str == null);
567*627f7eb2Smrg     o4.i = -44; o4.str = "x4";
568*627f7eb2Smrg 
569*627f7eb2Smrg     a.value5 = 45;
570*627f7eb2Smrg     assert(o4.i == -44 && !o4.l && o4.str == "x4");
571*627f7eb2Smrg     assert(o5.i == 45 && !o5.l && o5.str == "str5");
572*627f7eb2Smrg     assert(!o6.i && !o6.l && o6.str == null);
573*627f7eb2Smrg     o5.i = -45; o5.str = "x5";
574*627f7eb2Smrg 
575*627f7eb2Smrg     a.value6 = 46;
576*627f7eb2Smrg     assert(o4.i == -44 && !o4.l && o4.str == "x4");
577*627f7eb2Smrg     assert(o5.i == -45 && !o4.l && o5.str == "x5");
578*627f7eb2Smrg     assert(!o6.i && o6.l == 46 && o6.str == "str6");
579*627f7eb2Smrg     o6.l = -46; o6.str = "x6";
580*627f7eb2Smrg 
581*627f7eb2Smrg     // disconnect the watchers and make sure it doesn't trigger
582*627f7eb2Smrg     a.s4.disconnect(&o4.watchInt);
583*627f7eb2Smrg     a.s5.disconnect(&o5.watchInt);
584*627f7eb2Smrg     a.s6.disconnect(&o6.watchLong);
585*627f7eb2Smrg 
586*627f7eb2Smrg     a.value4 = 54;
587*627f7eb2Smrg     a.value5 = 55;
588*627f7eb2Smrg     a.value6 = 56;
589*627f7eb2Smrg     assert(o4.i == -44 && !o4.l && o4.str == "x4");
590*627f7eb2Smrg     assert(o5.i == -45 && !o4.l && o5.str == "x5");
591*627f7eb2Smrg     assert(!o6.i && o6.l == -46 && o6.str == "x6");
592*627f7eb2Smrg 
593*627f7eb2Smrg     // reconnect the watcher and make sure it triggers
594*627f7eb2Smrg     a.s4.connect(&o4.watchInt);
595*627f7eb2Smrg     a.s5.connect(&o5.watchInt);
596*627f7eb2Smrg     a.s6.connect(&o6.watchLong);
597*627f7eb2Smrg 
598*627f7eb2Smrg     a.value4 = 64;
599*627f7eb2Smrg     a.value5 = 65;
600*627f7eb2Smrg     a.value6 = 66;
601*627f7eb2Smrg     assert(o4.i == 64 && !o4.l && o4.str == "str4");
602*627f7eb2Smrg     assert(o5.i == 65 && !o4.l && o5.str == "str5");
603*627f7eb2Smrg     assert(!o6.i && o6.l == 66 && o6.str == "str6");
604*627f7eb2Smrg 
605*627f7eb2Smrg     // destroy observers
606*627f7eb2Smrg     destroy(o4);
607*627f7eb2Smrg     destroy(o5);
608*627f7eb2Smrg     destroy(o6);
609*627f7eb2Smrg     a.value4 = 44;
610*627f7eb2Smrg     a.value5 = 45;
611*627f7eb2Smrg     a.value6 = 46;
612*627f7eb2Smrg }
613*627f7eb2Smrg 
614*627f7eb2Smrg // Triggers bug from issue 15341
615*627f7eb2Smrg @system unittest
616*627f7eb2Smrg {
617*627f7eb2Smrg     class Observer
618*627f7eb2Smrg     {
watch()619*627f7eb2Smrg        void watch() { }
watch2()620*627f7eb2Smrg        void watch2() { }
621*627f7eb2Smrg     }
622*627f7eb2Smrg 
623*627f7eb2Smrg     class Bar
624*627f7eb2Smrg     {
625*627f7eb2Smrg        mixin Signal!();
626*627f7eb2Smrg     }
627*627f7eb2Smrg 
628*627f7eb2Smrg    auto a = new Bar;
629*627f7eb2Smrg    auto o = new Observer;
630*627f7eb2Smrg 
631*627f7eb2Smrg    //Connect both observer methods for the same instance
632*627f7eb2Smrg    a.connect(&o.watch);
633*627f7eb2Smrg    a.connect(&o.watch2); // not connecting watch2() or disconnecting it manually fixes the issue
634*627f7eb2Smrg 
635*627f7eb2Smrg    //Disconnect a single method of the two
636*627f7eb2Smrg    a.disconnect(&o.watch); // NOT disconnecting watch() fixes the issue
637*627f7eb2Smrg 
638*627f7eb2Smrg    destroy(o); // destroying o should automatically call unhook and disconnect the slot for watch2
639*627f7eb2Smrg    a.emit(); // should not raise segfault since &o.watch2 is no longer connected
640*627f7eb2Smrg }
641*627f7eb2Smrg 
version(none)642*627f7eb2Smrg version (none) // Disabled because of dmd @@@BUG5028@@@
643*627f7eb2Smrg @system unittest
644*627f7eb2Smrg {
645*627f7eb2Smrg     class A
646*627f7eb2Smrg     {
647*627f7eb2Smrg         mixin Signal!(string, int) s1;
648*627f7eb2Smrg     }
649*627f7eb2Smrg 
650*627f7eb2Smrg     class B : A
651*627f7eb2Smrg     {
652*627f7eb2Smrg         mixin Signal!(string, int) s2;
653*627f7eb2Smrg     }
654*627f7eb2Smrg }
655*627f7eb2Smrg 
656*627f7eb2Smrg // Triggers bug from issue 16249
657*627f7eb2Smrg @system unittest
658*627f7eb2Smrg {
659*627f7eb2Smrg     class myLINE
660*627f7eb2Smrg     {
661*627f7eb2Smrg         mixin Signal!( myLINE, int );
662*627f7eb2Smrg 
value(int v)663*627f7eb2Smrg         void value( int v )
664*627f7eb2Smrg         {
665*627f7eb2Smrg             if ( v >= 0 ) emit( this, v );
666*627f7eb2Smrg             else          emit( new myLINE, v );
667*627f7eb2Smrg         }
668*627f7eb2Smrg     }
669*627f7eb2Smrg 
670*627f7eb2Smrg     class Dot
671*627f7eb2Smrg     {
672*627f7eb2Smrg         int value;
673*627f7eb2Smrg 
674*627f7eb2Smrg         myLINE line_;
line(myLINE line_x)675*627f7eb2Smrg         void line( myLINE line_x )
676*627f7eb2Smrg         {
677*627f7eb2Smrg             if ( line_ is line_x ) return;
678*627f7eb2Smrg 
679*627f7eb2Smrg             if ( line_ !is null )
680*627f7eb2Smrg             {
681*627f7eb2Smrg                 line_.disconnect( &watch );
682*627f7eb2Smrg             }
683*627f7eb2Smrg             line_ = line_x;
684*627f7eb2Smrg             line_.connect( &watch );
685*627f7eb2Smrg         }
686*627f7eb2Smrg 
watch(myLINE line_x,int value_x)687*627f7eb2Smrg         void watch( myLINE line_x, int value_x )
688*627f7eb2Smrg         {
689*627f7eb2Smrg             line = line_x;
690*627f7eb2Smrg             value = value_x;
691*627f7eb2Smrg         }
692*627f7eb2Smrg     }
693*627f7eb2Smrg 
694*627f7eb2Smrg     auto dot1 = new Dot;
695*627f7eb2Smrg     auto dot2 = new Dot;
696*627f7eb2Smrg     auto line = new myLINE;
697*627f7eb2Smrg     dot1.line = line;
698*627f7eb2Smrg     dot2.line = line;
699*627f7eb2Smrg 
700*627f7eb2Smrg     line.value = 11;
701*627f7eb2Smrg     assert( dot1.value == 11 );
702*627f7eb2Smrg     assert( dot2.value == 11 );
703*627f7eb2Smrg 
704*627f7eb2Smrg     line.value = -22;
705*627f7eb2Smrg     assert( dot1.value == -22 );
706*627f7eb2Smrg     assert( dot2.value == -22 );
707*627f7eb2Smrg }
708*627f7eb2Smrg 
709