1 module des.util.arch.sig;
2 
3 import des.util.arch.emm;
4 import des.util.arch.slot;
5 
6 ///
7 class SignalException : Exception
8 { this( string m ) @safe pure nothrow { super(m); } }
9 
10 ///
11 template isSignal(T)
12 {
13     enum isSignal = is( typeof( impl(T.init) ) );
14     void impl(Args...)( Signal!(Args) ) {}
15 }
16 
17 ///
18 class Signal(Args...) : SignalLeverage, ExternalMemoryManager
19 {
20     mixin EMM;
21 
22 protected:
23 
24     ///
25     alias Slot!Args TSlot;
26 
27     ///
28     TSlot[] slots;
29 
30 public:
31 
32     ///
33     TSlot connect( TSlot slot )
34     in { assert( slot !is null ); }
35     body
36     {
37         if( !connected(slot) )
38         {
39             slots ~= slot;
40             slot.control.connect( this );
41         }
42         return slot;
43     }
44 
45     ///
46     void disconnect( TSlot slot )
47     in { assert( slot !is null ); }
48     body
49     {
50         size_t i = indexOf( slot );
51         if( i == -1 ) return;
52         slots = slots[0..i] ~ ( i != slots.length-1 ? slots[i..$] : [] );
53         slot.control.disconnect( this );
54     }
55 
56     ///
57     override void disconnect( SlotController sc )
58     in { assert( sc !is null ); }
59     body
60     {
61         TSlot[] buf;
62         SlotController[] dis;
63         foreach( slot; slots )
64             if( sc != slot.control ) buf ~= slot;
65             else dis ~= slot.control;
66         slots = buf;
67         foreach( s; dis ) s.disconnect(this);
68     }
69 
70     ///
71     void disconnect( SlotHandler handler )
72     { disconnect( handler.slotController ); }
73 
74     ///
75     void opCall( Args args ) { foreach( slot; slots ) slot(args); }
76 
77 protected:
78 
79     ///
80     ptrdiff_t indexOf( TSlot slot )
81     {
82         foreach( i, cs; slots )
83             if( cs == slot )
84                 return i;
85         return -1;
86     }
87 
88     ///
89     bool connected( TSlot slot )
90     { return indexOf(slot) != -1; }
91 
92     void selfDestroy()
93     {
94         foreach( slot; slots )
95             slot.control.disconnect( this );
96     }
97 }
98 ///
99 unittest
100 {
101     string[] messages;
102     string[] human_readed;
103     string[] robot_readed;
104     string[] cliend_okdas;
105 
106     class Postal : ExternalMemoryManager
107     {
108         mixin EMM;
109         Signal!string onMessage;
110         this() { onMessage = newEMM!(Signal!string); }
111         void message( string msg )
112         {
113             messages ~= msg;
114             onMessage( msg );
115         }
116     }
117 
118     class Client : SlotHandler, ExternalMemoryManager
119     {
120         mixin EMM;
121 
122         SlotController sc;
123         Slot!string read_slot;
124         Slot!string okda_slot;
125 
126         this()
127         {
128             sc = newEMM!SlotController;
129             read_slot = newEMM!(Slot!string)(this,&read);
130             okda_slot = newEMM!(Slot!string)(this,&okda);
131         }
132 
133         SlotController slotController() @property { return sc; }
134 
135         abstract void read( string msg );
136 
137         void okda( string msg ) { cliend_okdas ~= msg; }
138     }
139 
140     auto human = new class Client
141     { override void read( string msg ) { human_readed ~= msg; } };
142 
143     auto robot = new class Client
144     { override void read( string msg ) { robot_readed ~= msg; } };
145 
146     auto postal = new Postal;
147 
148     postal.message( "test" );
149     assert( messages.length == 1 );
150     assert( messages[0] == "test" );
151     assert( human_readed.length == 0 );
152     assert( robot_readed.length == 0 );
153     assert( cliend_okdas.length == 0 );
154 
155     postal.onMessage.connect( human.read_slot );
156     postal.onMessage.connect( human.read_slot );
157     postal.onMessage.connect( human.okda_slot );
158 
159     postal.message( "hello" );
160     assert( messages.length == 2 );
161     assert( human_readed.length == 1 );
162     assert( human_readed[0] == "hello" );
163     assert( robot_readed.length == 0 );
164     assert( cliend_okdas.length == 1 );
165 
166     postal.onMessage.connect( robot.read_slot );
167 
168     postal.message( "tech" );
169     assert( messages.length == 3 );
170     assert( human_readed.length == 2 );
171     assert( robot_readed.length == 1 );
172     assert( robot_readed[0] == "tech" );
173     assert( cliend_okdas.length == 2 );
174 
175     postal.onMessage.disconnect( human );
176 
177     postal.message( "tech2" );
178     assert( messages.length == 4 );
179     assert( human_readed.length == 2 );
180     assert( robot_readed.length == 2 );
181     assert( cliend_okdas.length == 2 );
182 
183     human.read( "ok" );
184     assert( human_readed.length == 3 );
185 
186     robot.destroy();
187 
188     postal.message( "tech3" );
189 
190     assert( messages.length == 5 );
191     assert( human_readed.length == 3 );
192     assert( robot_readed.length == 2 );
193     assert( cliend_okdas.length == 2 );
194 
195     postal.onMessage.connect( human.read_slot );
196     postal.onMessage.connect( human.okda_slot );
197 
198     postal.message( "bb" );
199     assert( messages.length == 6 );
200     assert( human_readed.length == 4 );
201     assert( robot_readed.length == 2 );
202     assert( cliend_okdas.length == 3 );
203 
204     postal.onMessage.disconnect( human.okda_slot );
205 
206     postal.message( "fbb" );
207     assert( messages.length == 7 );
208     assert( human_readed.length == 5 );
209     assert( robot_readed.length == 2 );
210     assert( cliend_okdas.length == 3 );
211 }
212 
213 ///
214 class SignalReverse( Args... ) : Signal!Args
215 {
216     ///
217     override void opCall( Args args )
218     { foreach_reverse( slot; slots ) slot( args ); }
219 }
220 
221 ///
222 class SignalBox( Args... ) : Signal!Args
223 {
224     this()
225     {
226         begin = newEMM!(Signal!Args);
227         end = newEMM!(SignalReverse!Args);
228     }
229 
230     ///
231     Signal!Args begin;
232     ///
233     SignalReverse!Args end;
234 
235     /++ calls:
236      +  0. begin
237      +  0. this
238      +  0. end
239      +/
240     override void opCall( Args args )
241     {
242         begin( args );
243         super.opCall( args );
244         end( args );
245     }
246 }
247 
248 unittest
249 {
250     static assert(  isSignal!( Signal!string ) );
251     static assert(  isSignal!( Signal!(float,int) ) );
252     static assert(  isSignal!( SignalReverse!string ) );
253     static assert(  isSignal!( SignalBox!(float,int) ) );
254     static assert( !isSignal!( string ) );
255     static assert( !isSignal!( int ) );
256     static assert( !isSignal!( Slot!int ) );
257 }
258