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