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