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 }