1 module carbon.event; 2 3 import std.algorithm, 4 std.variant, 5 std.traits, 6 std.signals; 7 8 version(unittest) import std.stdio; 9 10 /** 11 12 */ 13 struct FiredContext 14 { 15 Variant sender; 16 string file; 17 size_t line; 18 string funcName; 19 string prettyFuncName; 20 } 21 22 23 /** 24 25 */ 26 interface StrongConnectedSlotTag {} 27 28 29 private 30 final class SignalImpl(T...) 31 { 32 void connect(string name, Class)(Class obj) 33 if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface))) 34 { 35 MixedInSignal.connect(mixin(`&obj.` ~ name)); 36 } 37 38 39 void disconnect(string name, Class)(Class obj) 40 if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface))) 41 { 42 MixedInSignal.disconnect(mixin(`&obj.` ~ name)); 43 } 44 45 46 SlotTag strongConnect(Callable)(Callable func) 47 if(is(typeof((T args) { func(args); }))) 48 { 49 static final class SlotImpl : SlotTag 50 { 51 override 52 void dg(T args){ _f(args); } 53 54 override 55 bool opEquals(Object rhs) 56 { 57 if(auto o = cast(SlotImpl)rhs) 58 return this._f == o._f; 59 else 60 return false; 61 } 62 63 private: 64 Callable _f; 65 } 66 67 auto slot = new SlotImpl; 68 slot._f = func; 69 _slotSet[slot] = true; 70 71 this.connect!"dg"(slot); 72 return slot; 73 } 74 75 76 void disconnect(ref SlotTag tag) 77 { 78 _slotSet.remove(tag); 79 destroy(tag); 80 tag = null; 81 } 82 83 84 void strongDisconnect(ref SlotTag tag) 85 { 86 this.disconnect(tag); 87 tag = null; 88 } 89 90 91 void emitImpl(T args) 92 { 93 MixedInSignal.emit(args); 94 } 95 96 97 private 98 { 99 mixin Signal!(T) MixedInSignal; 100 } 101 102 103 interface SlotTag : StrongConnectedSlotTag 104 { 105 void dg(T); 106 } 107 108 private: 109 bool[SlotTag] _slotSet; 110 } 111 112 113 114 /** 115 116 */ 117 final class EventManager(T...) 118 { 119 this() 120 { 121 _noarg = new SignalImpl!(); 122 _simple = new SignalImpl!T; 123 _withContext = new SignalImpl!(FiredContext, T); 124 } 125 126 127 void disable() 128 { 129 _disabled = true; 130 } 131 132 133 void enable() 134 { 135 _disabled = false; 136 } 137 138 139 void connect(string name, Class)(Class obj) 140 if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface))) 141 { 142 static if(is(typeof((FiredContext ctx, T args){ mixin(`obj.` ~ name ~ `(ctx, args);`); }))) 143 _withContext.connect!name(obj); 144 else static if(is(typeof((T args){ mixin(`obj.` ~ name ~ `(args);`); }))) 145 _simple.connect!name(obj); 146 else 147 _noarg.connect!name(obj); 148 } 149 150 151 void disconnect(string name, Class)(Class obj) 152 if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface))) 153 { 154 static if(is(typeof((FiredContext ctx, T args){ mixin(`obj.` ~ name ~ `(ctx, args);`); }))) 155 _withContext.disconnect!name(obj); 156 else static if(is(typeof((T args){ mixin(`obj.` ~ name ~ `(args);`); }))) 157 _simple.disconnect!name(obj); 158 else 159 _noarg.disconnect!name(obj); 160 } 161 162 163 StrongConnectedSlotTag strongConnect(void function() func) 164 { 165 return _noarg.strongConnect(func); 166 } 167 168 169 StrongConnectedSlotTag strongConnect(void function(T) func) 170 { 171 return _simple.strongConnect(func); 172 } 173 174 175 StrongConnectedSlotTag strongConnect(void function(FiredContext, T) func) 176 { 177 return _withContext.strongConnect(func); 178 } 179 180 181 StrongConnectedSlotTag strongConnect(void delegate() func) 182 { 183 return _noarg.strongConnect(func); 184 } 185 186 187 StrongConnectedSlotTag strongConnect(void delegate(T) func) 188 { 189 return _simple.strongConnect(func); 190 } 191 192 193 StrongConnectedSlotTag strongConnect(void delegate(FiredContext, T) func) 194 { 195 return _withContext.strongConnect(func); 196 } 197 198 199 StrongConnectedSlotTag strongConnect(Callable)(Callable func) 200 { 201 static if(is(typeof((FiredContext ctx, T args){ func(ctx, args); }))) 202 return _withContext.strongConnect(func); 203 else static if(is(typeof((T args){ func(args); }))) 204 return _simple.strongConnect(func); 205 else 206 return _noarg.strongConnect(func); 207 } 208 209 210 void strongDisconnect(ref StrongConnectedSlotTag slotTag) 211 { 212 if(auto s1 = cast(_withContext.SlotTag)slotTag){ 213 _withContext.strongDisconnect(s1); 214 slotTag = null; 215 } 216 else if(auto s2 = cast(_simple.SlotTag)slotTag){ 217 _simple.strongDisconnect(s2); 218 slotTag = null; 219 } 220 else if(auto s3 = cast(_noarg.SlotTag)slotTag){ 221 _noarg.strongDisconnect(s3); 222 slotTag = null; 223 } 224 } 225 226 227 void disconnect(ref StrongConnectedSlotTag slotTag) 228 { 229 this.strongDisconnect(slotTag); 230 slotTag = null; 231 } 232 233 234 void emit()(T args, string file = __FILE__, size_t line = __LINE__, 235 string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__) 236 { 237 emit(null, args, file, line, func, preFunc); 238 } 239 240 241 void emit(S)(S sender, T args, string file = __FILE__, size_t line = __LINE__, 242 string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__) 243 { 244 FiredContext ctx; 245 ctx.sender = sender; 246 ctx.file = file; 247 ctx.line = line; 248 ctx.funcName = func; 249 ctx.prettyFuncName = preFunc; 250 251 emit(ctx, args); 252 } 253 254 255 void emit()(FiredContext ctx, T args) 256 { 257 if(!_disabled){ 258 _noarg.emit(); 259 _simple.emit(args); 260 _withContext.emit(ctx, args); 261 } 262 } 263 264 265 private: 266 bool _disabled; 267 SignalImpl!() _noarg; 268 SignalImpl!T _simple; 269 SignalImpl!(FiredContext, T) _withContext; 270 } 271 272 273 /// 274 unittest 275 { 276 auto event = new EventManager!int(); 277 278 int sum; 279 auto tag1 = event.strongConnect(delegate(a){ sum += a; }); 280 281 event.emit(12); 282 assert(sum == 12); 283 284 auto tag2 = event.strongConnect(() { sum += 2; }); 285 286 event.emit(4); 287 assert(sum == 18); // add 2 + 4 288 289 event.disconnect(tag1); 290 event.emit(12); 291 assert(sum == 20); // only add 2 292 293 event.disconnect(tag2); 294 event.emit(5); 295 assert(sum == 20); 296 } 297 298 299 unittest 300 { 301 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 302 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 303 304 auto event = new EventManager!bool(); 305 306 bool bCalled = false; 307 auto tag = event.strongConnect(delegate(FiredContext ctx, bool b){ 308 assert(b); 309 assert(ctx.sender == null); 310 bCalled = true; 311 }); 312 313 event.emit(true); 314 assert(bCalled); 315 316 bCalled = false; 317 event.disable(); 318 event.emit(true); 319 assert(!bCalled); 320 321 event.enable(); 322 event.emit(true); 323 assert(bCalled); 324 325 bCalled = false; 326 event.disconnect(tag); 327 event.emit(true); 328 assert(!bCalled); 329 assert(tag is null); 330 } 331 332 333 /** 334 335 */ 336 class SeqEventManager(size_t N, T...) 337 { 338 this() 339 { 340 foreach(i; 0 .. N) 341 _signals[i] = new EventManager!T; 342 } 343 344 345 EventManager!T opIndex(size_t i) 346 in{ 347 assert(i < N); 348 } 349 body{ 350 return _signals[i]; 351 } 352 353 354 void disable() 355 { 356 _disable = true; 357 } 358 359 360 void enable() 361 { 362 _disable = false; 363 } 364 365 366 void emit()(auto ref T args, string file = __FILE__, size_t line = __LINE__, 367 string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__) 368 { 369 emit(null, forward!args, file, line, func, preFunc); 370 } 371 372 373 void emit(S)(S sender, auto ref T args, string file = __FILE__, size_t line = __LINE__, 374 string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__) 375 { 376 FiredContext ctx; 377 ctx.sender = sender; 378 ctx.file = file; 379 ctx.line = line; 380 ctx.funcName = func; 381 ctx.prettyFuncName = preFunc; 382 383 emit(ctx, forward!args); 384 } 385 386 387 void emit()(FiredContext ctx, auto ref T args) 388 { 389 if(!_disable){ 390 foreach(i, ref e; _signals) 391 e.emit(ctx, forward!args); 392 } 393 } 394 395 396 private: 397 EventManager!T[N] _signals; 398 bool _disable; 399 } 400 401 /// 402 unittest 403 { 404 scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();} 405 scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();} 406 407 auto event = new SeqEventManager!(3, bool); 408 409 size_t cnt; 410 size_t[3] ns; 411 event[0].strongConnect(delegate(b){ 412 assert(b); 413 ns[0] = cnt; 414 ++cnt; 415 }); 416 417 event[1].strongConnect(delegate(b){ 418 assert(b); 419 ns[1] = cnt; 420 ++cnt; 421 }); 422 423 event[2].strongConnect(delegate(b){ 424 assert(b); 425 ns[2] = cnt; 426 ++cnt; 427 }); 428 429 event.emit(true); 430 assert(cnt == 3); 431 assert(ns[] == [0, 1, 2]); 432 }