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