1 module util.signals; 2 3 /** 4 * Signals and Slots are an implementation of the Observer Pattern. 5 * Essentially, when a Signal is emitted, a list of connected Observers 6 * (called slots) are called. 7 * 8 * There have been several D implementations of Signals and Slots. 9 * This version makes use of several new features in D, which make 10 * using it simpler and less error prone. In particular, it is no 11 * longer necessary to instrument the slots. 12 * 13 * References: 14 * $(LINK2 http://scottcollins.net/articles/a-deeper-look-at-_signals-and-slots.html, A Deeper Look at Signals and Slots)$(BR) 15 * $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR) 16 * $(LINK2 http://en.wikipedia.org/wiki/Signals_and_slots, Wikipedia)$(BR) 17 * $(LINK2 http://boost.org/doc/html/$(SIGNALS).html, Boost Signals)$(BR) 18 * $(LINK2 http://doc.trolltech.com/4.1/signalsandslots.html, Qt)$(BR) 19 * 20 * There has been a great deal of discussion in the D newsgroups 21 * over this, and several implementations: 22 * 23 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/signal_slots_library_4825.html, signal slots library)$(BR) 24 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Signals_and_Slots_in_D_42387.html, Signals and Slots in D)$(BR) 25 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dynamic_binding_--_Qt_s_Signals_and_Slots_vs_Objective-C_42260.html, Dynamic binding -- Qt's Signals and Slots vs Objective-C)$(BR) 26 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dissecting_the_SS_42377.html, Dissecting the SS)$(BR) 27 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/dwt/about_harmonia_454.html, about harmonia)$(BR) 28 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/1502.html, Another event handling module)$(BR) 29 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/41825.html, Suggestion: signal/slot mechanism)$(BR) 30 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/13251.html, Signals and slots?)$(BR) 31 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/10714.html, Signals and slots ready for evaluation)$(BR) 32 * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/1393.html, Signals & Slots for Walter)$(BR) 33 * $(LINK2 http://www.digitalmars.com/d/archives/28456.html, Signal/Slot mechanism?)$(BR) 34 * $(LINK2 http://www.digitalmars.com/d/archives/19470.html, Modern Features?)$(BR) 35 * $(LINK2 http://www.digitalmars.com/d/archives/16592.html, Delegates vs interfaces)$(BR) 36 * $(LINK2 http://www.digitalmars.com/d/archives/16583.html, The importance of component programming (properties, signals and slots, etc))$(BR) 37 * $(LINK2 http://www.digitalmars.com/d/archives/16368.html, signals and slots)$(BR) 38 * 39 * Bugs: 40 * Not safe for multiple threads operating on the same signals 41 * or slots. 42 * 43 * Safety of handlers is not yet enforced 44 * Macros: 45 * WIKI = Phobos/StdSignals 46 * SIGNALS=signals 47 * 48 * Copyright: Copyright Digital Mars 2000 - 2009. 49 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 50 * Authors: $(WEB digitalmars.com, Walter Bright), 51 * Johannes Pfau 52 */ 53 /* Copyright Digital Mars 2000 - 2009. 54 * Distributed under the Boost Software License, Version 1.0. 55 * (See accompanying file LICENSE_1_0.txt or copy at 56 * http://www.boost.org/LICENSE_1_0.txt) 57 */ 58 //~ module signals; 59 60 import std.algorithm; //find 61 import std.container; //SList 62 import std.functional; //toDelegate 63 import std.range; //take 64 import std.traits; //isPointer, isSafe 65 66 //Bug 4536 67 private template Init(T...) 68 { 69 T Init; 70 } 71 72 template isHandlerDelegate(T, Types...) 73 { 74 static if(is(T == delegate)) 75 { 76 enum bool isHandlerDelegate = (is(typeof(T.init(Init!(Types)))) 77 && (is(ReturnType!(T) == void) 78 || is(ReturnType!(T) == bool))); 79 } 80 else 81 { 82 enum bool isHandlerDelegate = false; 83 } 84 } 85 86 template isHandlerFunction(T, Types...) 87 { 88 static if(isPointer!(T)) 89 { 90 enum bool isHandlerFunction = (isPointer!(T) && is(pointerTarget!(T) == function) 91 && is(typeof(T.init(Init!(Types)))) 92 && (is(ReturnType!(T) == void) 93 || is(ReturnType!(T) == bool))); 94 } 95 else 96 { 97 enum bool isHandlerFunction = false; 98 } 99 } 100 101 template isHandlerStruct(T, Types...) 102 { 103 static if(isPointer!(T)) 104 { 105 enum bool isHandlerStruct = (is(pointerTarget!(T) == struct) 106 //&& (isSafe!(pointerTarget!(T).init)) 107 && is(typeof(pointerTarget!(T).init.opCall(Init!(Types)))) 108 && (is(ReturnType!(T) == void) 109 || is(ReturnType!(T) == bool))); 110 } 111 else 112 { 113 enum bool isHandlerStruct = false; 114 } 115 } 116 117 unittest 118 { 119 struct tmp 120 { 121 @safe bool opCall() {return false;} 122 } 123 tmp* a = new tmp(); 124 125 assert(isHandlerStruct!(typeof(a))); 126 tmp b; 127 assert(!isHandlerStruct!(typeof(b))); 128 } 129 130 //Checking for unsafe handlers is not yet implemented 131 /* 132 unittest 133 { 134 struct tmp 135 { 136 bool opCall() {return false;} 137 } 138 tmp* a = new tmp(); 139 140 assert(!isHandlerStruct!(typeof(a))); 141 tmp b; 142 assert(!isHandlerStruct!(typeof(b))); 143 } 144 */ 145 146 template isHandlerClass(T, Types...) 147 { 148 enum bool isHandlerClass = (is(T == class) 149 && is(typeof(T.init.opCall(Init!(Types)))) 150 && (is(ReturnType!(T) == void) 151 || is(ReturnType!(T) == bool))); 152 } 153 154 template isHandler(T, Types...) 155 { 156 enum bool isHandler = isHandlerDelegate!(T, Types) || isHandlerFunction!(T, Types) 157 || isHandlerClass!(T, Types) || isHandlerStruct!(T, Types); 158 } 159 160 unittest 161 { 162 struct tmp 163 { 164 @safe void opCall(){} 165 } 166 struct tmp2 167 { 168 @safe char opCall(){ return 'c';} 169 } 170 struct tmp3 171 { 172 @safe bool opCall(){ return true;} 173 } 174 tmp* a = new tmp(); 175 176 assert(isHandler!(typeof(a))); 177 assert(!isHandler!(typeof(a), int)); 178 assert(!isHandler!(typeof(a), int, bool)); 179 assert(!isHandler!(typeof(a), int, bool, string)); 180 181 tmp b; 182 assert(!isHandler!(typeof(b))); 183 assert(!isHandler!(typeof(b), int)); 184 assert(!isHandler!(typeof(b), int, bool)); 185 assert(!isHandler!(typeof(b), int, bool, string)); 186 187 tmp2 c; 188 assert(!isHandler!(typeof(c))); 189 assert(!isHandler!(typeof(c), int)); 190 assert(!isHandler!(typeof(c), int, bool)); 191 assert(!isHandler!(typeof(c), int, bool, string)); 192 193 tmp3* d = new tmp3(); 194 assert(isHandler!(typeof(d))); 195 assert(!isHandler!(typeof(d), int)); 196 assert(!isHandler!(typeof(d), int, bool)); 197 assert(!isHandler!(typeof(d), int, bool, string)); 198 } 199 200 unittest 201 { 202 class tmp 203 { 204 @safe void opCall(int i){} 205 } 206 class tmp2 207 { 208 @safe char opCall(string a, bool b){ return 'c';} 209 } 210 class tmp3 211 { 212 @safe bool opCall(char b){ return true;} 213 } 214 tmp a = new tmp(); 215 216 assert(!isHandler!(typeof(a))); 217 assert(isHandler!(typeof(a), int)); 218 assert(!isHandler!(typeof(a), int, bool)); 219 assert(!isHandler!(typeof(a), int, bool, string)); 220 221 tmp2 b = new tmp2(); 222 assert(!isHandler!(typeof(b))); 223 assert(!isHandler!(typeof(b), string, bool)); 224 assert(!isHandler!(typeof(b), int, bool)); 225 assert(!isHandler!(typeof(b), int, bool, string)); 226 227 tmp3 c = new tmp3(); 228 assert(!isHandler!(typeof(c))); 229 assert(isHandler!(typeof(c), char)); 230 assert(!isHandler!(typeof(c), int, bool)); 231 assert(!isHandler!(typeof(c), int, bool, string)); 232 } 233 234 unittest 235 { 236 static @safe void test(int a, int b) {}; 237 static @safe void test2(int b) {}; 238 static @safe bool test3(int a, int b) {return true;}; 239 static @safe bool test4(int b) {return true;}; 240 assert(isHandler!(typeof(&test), int, int)); 241 assert(!isHandler!(typeof(&test))); 242 assert(isHandler!(typeof(&test2), int)); 243 assert(!isHandler!(typeof(&test2, string))); 244 assert(isHandler!(typeof(&test3), int, int)); 245 assert(!isHandler!(typeof(&test3), bool)); 246 assert(isHandler!(typeof(&test4), int)); 247 assert(!isHandler!(typeof(&test4))); 248 } 249 250 unittest 251 { 252 @safe void test(int a, int b) {}; 253 @safe void test2(int b) {}; 254 @safe bool test3(int a, int b) {return true;}; 255 @safe bool test4(int b) {return true;}; 256 257 assert(isHandler!(typeof(&test), int, int)); 258 assert(!isHandler!(typeof(&test))); 259 assert(isHandler!(typeof(&test2), int)); 260 assert(!isHandler!(typeof(&test2, string))); 261 assert(isHandler!(typeof(&test3), int, int)); 262 assert(!isHandler!(typeof(&test3), bool)); 263 assert(isHandler!(typeof(&test4), int)); 264 assert(!isHandler!(typeof(&test4))); 265 } 266 267 /** 268 * This Signal struct is an implementation of the Observer pattern. 269 * 270 * All D callable types (functions, delegates, structs with opCall, 271 * classes with opCall) can be registered with a signal. When the signal 272 * occurs all assigned callables are called. 273 * 274 * Structs with opCall are only supported if they're passed by pointer. These 275 * structs are then expected to be allocated on the heap. 276 * 277 * Delegates to struct instances or nested functions are supported. You 278 * have to make sure to disconnect these delegates from the Signal before 279 * they go out of scope though. 280 * 281 * The return type of the handlers must be void or bool. If the return 282 * type is bool and the handler returns false the remaining handlers are 283 * not called. If true is returned or the type is void the remaining 284 * handlers are called. 285 * 286 * SafeD: 287 * This Signal template can be used in safeD; all public functions 288 * are @safe or @trusted. All handlers connected to 289 * a signal must be @safe or @trusted. It's currently not possible to 290 * enforce the safety of the handlers, but it will be enforced as soon 291 * as possible. 292 * 293 * Examples: 294 * ------------------------------------------------------------------- 295 * import std.stdio; 296 * import std.signals; 297 * 298 * //same for classes 299 * struct A 300 * { 301 * string payload; 302 * @safe bool opCall(float f, string s) 303 * { 304 * writefln("A: %f:%s:%s", f, s, payload); 305 * return true; 306 * } 307 * } 308 * 309 * @safe void testFunc(float f, string s) 310 * { 311 * writefln("Function: %f:%s", f, s); 312 * } 313 * 314 * Signal!(float, string) onTest; 315 * 316 * void main() 317 * { 318 * A* a = new A(); 319 * a.payload = "test payload"; 320 * onTest.connect(a); 321 * onTest ~= &testFunc; 322 * onTest(0.123f, "first call"); 323 * } 324 * ------------------------------------------------------------------- 325 */ 326 public struct Signal(Types...) 327 { 328 private: 329 //A slot is implemented as a delegate. The slot_t is the type of the delegate. 330 alias bool delegate(Types) slot_t; 331 //Same as slot_t but with void return type 332 alias void delegate(Types) void_slot_t; 333 334 /* This struct stores one delegate and information whether the 335 * delegate returns a bool or void */ 336 static struct Callable 337 { 338 //The void_slot_t delegate 339 slot_t deleg; 340 bool returnsBool = true; 341 342 this(void_slot_t del) 343 { 344 this.deleg = cast(slot_t)del; 345 this.returnsBool = false; 346 } 347 this(slot_t del) 348 { 349 this.deleg = del; 350 } 351 } 352 353 SList!(Callable) handlers; 354 355 /* 356 * Get a Callable for the handler. 357 * Handler can be a void function, void delegate, bool 358 * function, bool delegate, class with opCall or a pointer to 359 * a struct with opCall. 360 */ 361 @trusted Callable getCallable(T)(T handler) if(isHandler!(T, Types)) 362 { 363 static if(isHandlerDelegate!(T, Types) && is(ReturnType!(T) == void)) 364 { 365 return Callable(cast(void_slot_t)handler); 366 } 367 else static if(isHandlerFunction!(T, Types) && is(ReturnType!(T) == void)) 368 { 369 void delegate(Types) call = toDelegate(cast(void function(Types))handler); 370 return Callable(call); 371 } 372 else static if(isHandlerDelegate!(T, Types) && is(ReturnType!(T) == bool)) 373 { 374 return Callable(cast(slot_t)handler); 375 } 376 else static if(isHandlerFunction!(T, Types) && is(ReturnType!(T) == bool)) 377 { 378 return Callable(toDelegate(cast(bool function(Types))handler)); 379 } 380 else static if(isHandlerStruct!(T, Types)) 381 { 382 static if(is(ReturnType!(T) == void)) 383 { 384 return Callable(cast(void_slot_t)&handler.opCall); 385 } 386 else static if(is(ReturnType!(T) == bool)) 387 { 388 return Callable(cast(slot_t)&handler.opCall); 389 } 390 else 391 { 392 static assert(false, "BUG: Internal error"); 393 } 394 } 395 else static if(isHandlerClass!(T, Types)) 396 { 397 static if(is(ReturnType!(T) == void)) 398 { 399 return Callable(cast(void_slot_t)&handler.opCall); 400 } 401 else static if(is(ReturnType!(T) == bool)) 402 { 403 return Callable(cast(slot_t)&handler.opCall); 404 } 405 else 406 { 407 static assert(false, "BUG: Internal error"); 408 } 409 } 410 else 411 { 412 static assert(false, "BUG: Input type not supported. " 413 "Please file a bug report."); 414 } 415 } 416 417 public: 418 /** 419 * Set to false to disable signal emission 420 * 421 * Examples: 422 * -------------------------------- 423 * bool called = false; 424 * @safe void handler() { called = true; } 425 * Signal!() onTest; 426 * onTest ~= &handler; 427 * onTest(); 428 * assert(called); 429 * called = false; 430 * onTest.enabled = false; 431 * onTest(); 432 * assert(!called); 433 * onTest.enabled = true; 434 * onTest(); 435 * assert(called); 436 * -------------------------------- 437 */ 438 @safe bool enabled = true; 439 440 /** 441 * Check whether a handler is already connected 442 * 443 * Examples: 444 * -------------------------------- 445 * @safe void handler() {}; 446 * Signal!() onTest; 447 * assert(!onTest.isConnected(&handler)); 448 * onTest ~= &handler; 449 * assert(onTest.isConnected(&handler)); 450 * onTest(); 451 * -------------------------------- 452 */ 453 @trusted bool isConnected(T)(T handler) if(isHandler!(T, Types)) 454 { 455 Callable call = getCallable(handler); 456 return !find(handlers[], call).empty; 457 } 458 459 /** 460 * Add a handler to the list of handlers to be called when emit() is called. 461 * The handler is added at the end of the list. 462 * 463 * Throws: 464 * Exception if handler is already registered 465 * (Only if asserts are enabled! Does not throw 466 * in release mode!) 467 * 468 * Returns: 469 * The handler that was passed in as a paramter 470 * 471 * Examples: 472 * -------------------------------- 473 * int val; 474 * string text; 475 * @safe void handler(int i, string t) 476 * { 477 * val = i; 478 * text = t; 479 * } 480 * 481 * Signal!(int, string) onTest; 482 * onTest.connect(&handler); 483 * onTest(1, "test"); 484 * assert(val == 1); 485 * assert(text == "test"); 486 * -------------------------------- 487 */ 488 @trusted T connect(T)(T handler) if(isHandler!(T, Types)) 489 { 490 Callable call = getCallable(handler); 491 assert(find(handlers[], call).empty, "Handler is already registered!"); 492 handlers.stableInsertAfter(handlers[], call); 493 return handler; 494 } 495 496 /** 497 * Add a handler to the list of handlers to be called when emit() is called. 498 * Add this handler at the top of the list, so it will be called before all 499 * other handlers. 500 * 501 * Throws: 502 * Exception if handler is already registered 503 * (Only if asserts are enabled! Does not throw 504 * in release mode!) 505 * 506 * Returns: 507 * The handler that was passed in as a paramter 508 * 509 * -------------------------------- 510 * bool firstCalled, secondCalled; 511 * @safe void handler1() {firstCalled = true;} 512 * @safe void handler2() 513 * { 514 * secondCalled = true; 515 * assert(firstCalled); 516 * } 517 * Signal!() onTest; 518 * onTest ~= &handler2; 519 * onTest.connectFirst(&handler1); 520 * onTest(); 521 * assert(firstCalled && secondCalled); 522 * -------------------------------- 523 */ 524 @trusted T connectFirst(T)(T handler) if(isHandler!(T, Types)) 525 { 526 Callable call = getCallable(handler); 527 assert(find(handlers[], call).empty, "Handler is already registered!"); 528 handlers.stableInsertFront(call); 529 return handler; 530 } 531 532 /** 533 * Add a handler to be called after another handler. 534 * Params: 535 * afterThis = The new attached handler will be called after this handler 536 * handler = The handler to be attached 537 * 538 * Throws: 539 * Exception if handler is already registered 540 * (Only if asserts are enabled! Does not throw 541 * in release mode!) 542 * 543 * Exception if afterThis is not registered 544 * (Always, even if asserts are disabled) 545 * 546 * Returns: 547 * The handler that has been connected 548 * 549 * Examples: 550 * -------------------------------- 551 * bool firstCalled, secondCalled, thirdCalled; 552 * @safe void handler1() {firstCalled = true;} 553 * @safe void handler2() 554 * { 555 * secondCalled = true; 556 * assert(firstCalled); 557 * assert(thirdCalled); 558 * } 559 * @safe void handler3() 560 * { 561 * thirdCalled = true; 562 * assert(firstCalled); 563 * assert(!secondCalled); 564 * } 565 * Signal!() onTest; 566 * onTest ~= &handler1; 567 * onTest ~= &handler2; 568 * auto h = onTest.connectAfter(&handler1, &handler3); 569 * assert(h == &handler3); 570 * onTest(); 571 * assert(firstCalled && secondCalled && thirdCalled); 572 * -------------------------------- 573 */ 574 @trusted T connectAfter(T, U)(T afterThis, U handler) 575 if(isHandler!(T, Types) && isHandler!(U, Types)) 576 { 577 Callable after = getCallable(afterThis); 578 Callable call = getCallable(handler); 579 auto location = find(handlers[], after); 580 if(location.empty) 581 { 582 throw new Exception("Handler 'afterThis' is not registered!"); 583 } 584 assert(find(handlers[], call).empty, "Handler is already registered!"); 585 handlers.stableInsertAfter(take(location, 1), call); 586 return handler; 587 } 588 589 /** 590 * Add a handler to be called before another handler. 591 * Params: 592 * beforeThis = The new attached handler will be called after this handler 593 * handler = The handler to be attached 594 * 595 * Throws: 596 * Exception if handler is already registered 597 * (Only if asserts are enabled! Does not throw 598 * in release mode!) 599 * 600 * Returns: 601 * The handler that has been connected 602 * 603 * Exception if beforeThis is not registered 604 * (Always, even if asserts are disabled) 605 * 606 * Examples: 607 * -------------------------------- 608 * bool firstCalled, secondCalled, thirdCalled; 609 * @safe void handler1() {firstCalled = true;} 610 * @safe void handler2() 611 * { 612 * secondCalled = true; 613 * assert(firstCalled); 614 * assert(!thirdCalled); 615 * } 616 * @safe void handler3() 617 * { 618 * thirdCalled = true; 619 * assert(firstCalled); 620 * assert(secondCalled); 621 * } 622 * Signal!() onTest; 623 * onTest ~= &handler1; 624 * onTest ~= &handler3; 625 * onTest.connectBefore(&handler3, &handler2); 626 * onTest(); 627 * assert(firstCalled && secondCalled && thirdCalled); 628 * -------------------------------- 629 */ 630 @trusted T connectBefore(T, U)(T beforeThis, U handler) 631 if(isHandler!(T, Types) && isHandler!(U, Types)) 632 { 633 Callable before = getCallable(beforeThis); 634 Callable call = getCallable(handler); 635 auto location = find(handlers[], before); 636 if(location.empty) 637 { 638 throw new Exception("Handler 'beforeThis' is not registered!"); 639 } 640 assert(find(handlers[], call).empty, "Handler is already registered!"); 641 //not exactly fast 642 uint length = walkLength(handlers[]); 643 uint pos = walkLength(location); 644 uint new_location = length - pos; 645 location = handlers[]; 646 if(new_location == 0) 647 handlers.stableInsertFront(call); 648 else 649 handlers.stableInsertAfter(take(location, new_location), call); 650 return handler; 651 } 652 653 /** 654 * Remove a handler from the list of handlers to be called when emit() is called. 655 * 656 * Throws: 657 * Exception if handler is not registered 658 * (Always, even if asserts are disabled) 659 * 660 * Returns: 661 * The handler that has been disconnected 662 * 663 * Examples: 664 * -------------------------------- 665 * @safe void handler() {}; 666 * Signal!() onTest; 667 * onTest.connect(&handler); 668 * onTest.disconnect(&handler); 669 * onTest.connect(&handler); 670 * onTest(); 671 * -------------------------------- 672 */ 673 @trusted T disconnect(T)(T handler) if(isHandler!(T, Types)) 674 { 675 Callable call = getCallable(handler); 676 auto pos = find(handlers[], call); 677 if(pos.empty) 678 { 679 throw new Exception("Handler is not connected"); 680 } 681 handlers.stableLinearRemove(take(pos, 1)); 682 return handler; 683 } 684 685 /** 686 * Remove all handlers from the signal 687 * 688 * Examples: 689 * -------------------------------- 690 * @safe void handler() {}; 691 * Signal!() onTest; 692 * assert(onTest.calculateLength() == 0); 693 * onTest.connect(&handler); 694 * assert(onTest.calculateLength() == 1); 695 * onTest.clear(); 696 * assert(onTest.calculateLength() == 0); 697 * onTest(); 698 * -------------------------------- 699 */ 700 @trusted void clear() 701 { 702 handlers.clear(); 703 } 704 705 /** 706 * Calculate the number of registered handlers 707 * 708 * Complexity: $(BIGOH n) 709 * 710 * Examples: 711 * -------------------------------- 712 * @safe void handler() {}; 713 * @safe void handler2() {}; 714 * Signal!() onTest; 715 * assert(onTest.calculateLength() == 0); 716 * onTest.connect(&handler); 717 * assert(onTest.calculateLength() == 1); 718 * onTest.connect(&handler2); 719 * assert(onTest.calculateLength() == 2); 720 * onTest.clear(); 721 * assert(onTest.calculateLength() == 0); 722 * onTest(); 723 * -------------------------------- 724 */ 725 @trusted uint calculateLength() 726 { 727 return walkLength(handlers[]); 728 } 729 730 /** 731 * Just like Signal.connect() 732 */ 733 @safe T opOpAssign(string op, T)(T rhs) if(op == "~" && isHandler!(T, Types)) 734 { 735 return connect!(T)(rhs); 736 } 737 738 /** 739 * Call the connected handlers as explained in the documentation 740 * for the signal struct. 741 * 742 * Throws: 743 * Exceptions thrown in the signal handlers 744 * 745 * Examples: 746 * -------------------------------- 747 * @safe void handler() {}; 748 * Signal!() onTest; 749 * onTest.connect(&handler); 750 * onTest.emit(); 751 * -------------------------------- 752 */ 753 @trusted void emit(Types params) 754 { 755 if(this.enabled) 756 { 757 foreach(callable; handlers[]) 758 { 759 if(callable.returnsBool) 760 { 761 slot_t del = cast(slot_t)callable.deleg; 762 if(!del(params)) 763 return; 764 } 765 else 766 { 767 void_slot_t del = cast(void_slot_t)callable.deleg; 768 del(params); 769 } 770 } 771 } 772 } 773 774 /** 775 * Just like emit() 776 */ 777 @trusted void opCall(Types params) 778 { 779 emit(params); 780 } 781 } 782 783 //unit tests 784 unittest 785 { 786 int val; 787 string text; 788 @safe void handler(int i, string t) 789 { 790 val = i; 791 text = t; 792 } 793 @safe static void handler2(int i, string t) 794 { 795 } 796 797 Signal!(int, string) onTest; 798 onTest.connect(&handler); 799 onTest.connect(&handler2); 800 onTest(1, "test"); 801 assert(val == 1); 802 assert(text == "test"); 803 onTest(99, "te"); 804 assert(val == 99); 805 assert(text == "te"); 806 } 807 808 unittest 809 { 810 @safe void handler() {} 811 Signal!() onTest; 812 onTest.connect(&handler); 813 bool thrown = false; 814 try 815 onTest.connect(&handler); 816 catch(Throwable) 817 thrown = true; 818 819 assert(thrown); 820 } 821 822 unittest 823 { 824 @safe void handler() {}; 825 Signal!() onTest; 826 onTest.connect(&handler); 827 onTest.disconnect(&handler); 828 onTest.connect(&handler); 829 onTest(); 830 } 831 832 unittest 833 { 834 bool called = false; 835 @safe void handler() { called = true; }; 836 Signal!() onTest; 837 onTest ~= &handler; 838 onTest.disconnect(&handler); 839 onTest ~= &handler; 840 onTest(); 841 assert(called); 842 } 843 844 unittest 845 { 846 class handler 847 { 848 @safe void opCall(int i) {} 849 } 850 851 struct handler2 852 { 853 @safe void opCall(int i) {} 854 } 855 Signal!(int) onTest; 856 onTest ~= new handler; 857 auto h = onTest ~= new handler2; 858 onTest(0); 859 onTest.disconnect(h); 860 } 861 862 unittest 863 { 864 __gshared bool called = false; 865 866 struct A 867 { 868 string payload; 869 870 @trusted void opCall(float f, string s) 871 { 872 assert(payload == "payload"); 873 assert(f == 0.1234f); 874 assert(s == "test call"); 875 called = true; 876 } 877 } 878 879 A* a = new A(); 880 a.payload = "payload"; 881 882 Signal!(float, string) onTest; 883 onTest.connect(a); 884 onTest(0.1234f, "test call"); 885 assert(called); 886 } 887 888 unittest 889 { 890 __gshared bool called; 891 struct A 892 { 893 string payload; 894 @trusted void opCall(float f, string s) 895 { 896 assert(payload == "payload 2"); 897 called = true; 898 } 899 } 900 901 A* a = new A(); 902 a.payload = "payload"; 903 904 Signal!(float, string) onTest; 905 onTest.connect(a); 906 A* b = new A(); 907 b.payload = "payload 2"; 908 onTest.connect(b); 909 onTest.disconnect(a); 910 onTest(0.1234f, "test call"); 911 assert(called); 912 } 913 914 unittest 915 { 916 struct A 917 { 918 @safe void opCall() {} 919 } 920 A* a = new A(); 921 922 Signal!() onTest; 923 onTest.connect(a); 924 bool thrown = false; 925 try 926 onTest.connect(a); 927 catch(Throwable) 928 thrown = true; 929 930 assert(thrown); 931 } 932 933 unittest 934 { 935 struct A 936 { 937 @safe void opCall() {} 938 } 939 A* a = new A(); 940 941 Signal!() onTest; 942 onTest.connect(a); 943 onTest.disconnect(a); 944 bool thrown = false; 945 try 946 onTest.disconnect(a); 947 catch(Throwable) 948 thrown = true; 949 950 assert(thrown); 951 } 952 953 unittest 954 { 955 struct A 956 { 957 @safe void opCall() {} 958 } 959 A* a = new A(); 960 961 Signal!() onTest; 962 bool thrown = false; 963 try 964 onTest.disconnect(a); 965 catch(Throwable) 966 thrown = true; 967 968 assert(thrown); 969 } 970 971 unittest 972 { 973 bool secondCalled = false; 974 @safe bool first(int i) {return false;} 975 @safe void second(int i) {secondCalled = true;} 976 Signal!(int) onTest; 977 onTest ~= &first; 978 onTest ~= &second; 979 onTest(0); 980 assert(!secondCalled); 981 onTest.disconnect(&first); 982 onTest ~= &first; 983 onTest(0); 984 assert(secondCalled); 985 } 986 987 unittest 988 { 989 @safe void second(int i) {} 990 Signal!(int) onTest; 991 auto t1 = onTest.getCallable(&second); 992 auto t2 = onTest.getCallable(&second); 993 auto t3 = onTest.getCallable(&second); 994 assert(t1 == t2); 995 assert(t2 == t3); 996 } 997 998 unittest 999 { 1000 bool called = false; 1001 @safe void handler() { called = true; }; 1002 Signal!() onTest; 1003 onTest ~= &handler; 1004 onTest(); 1005 assert(called); 1006 called = false; 1007 onTest.enabled = false; 1008 onTest(); 1009 assert(!called); 1010 onTest.enabled = true; 1011 onTest(); 1012 assert(called); 1013 } 1014 1015 unittest 1016 { 1017 @safe void handler() {}; 1018 Signal!() onTest; 1019 assert(!onTest.isConnected(&handler)); 1020 onTest ~= &handler; 1021 assert(onTest.isConnected(&handler)); 1022 onTest(); 1023 assert(onTest.isConnected(&handler)); 1024 onTest.disconnect(&handler); 1025 assert(!onTest.isConnected(&handler)); 1026 onTest(); 1027 assert(!onTest.isConnected(&handler)); 1028 } 1029 1030 unittest 1031 { 1032 bool firstCalled, secondCalled, thirdCalled; 1033 @safe void handler1() {firstCalled = true;} 1034 @safe void handler2() 1035 { 1036 secondCalled = true; 1037 assert(firstCalled); 1038 assert(thirdCalled); 1039 } 1040 @safe void handler3() 1041 { 1042 thirdCalled = true; 1043 assert(firstCalled); 1044 assert(!secondCalled); 1045 } 1046 Signal!() onTest; 1047 onTest ~= &handler1; 1048 onTest ~= &handler2; 1049 auto h = onTest.connectAfter(&handler1, &handler3); 1050 assert(h == &handler3); 1051 onTest(); 1052 assert(firstCalled && secondCalled && thirdCalled); 1053 } 1054 1055 unittest 1056 { 1057 bool firstCalled, secondCalled; 1058 @safe void handler1() {firstCalled = true;} 1059 @safe void handler2() 1060 { 1061 secondCalled = true; 1062 assert(firstCalled); 1063 } 1064 Signal!() onTest; 1065 onTest ~= &handler2; 1066 onTest.connectFirst(&handler1); 1067 onTest(); 1068 assert(firstCalled && secondCalled); 1069 } 1070 1071 unittest 1072 { 1073 bool firstCalled, secondCalled, thirdCalled; 1074 @safe void handler1() {firstCalled = true;} 1075 @safe void handler2() 1076 { 1077 secondCalled = true; 1078 assert(firstCalled); 1079 assert(!thirdCalled); 1080 } 1081 @safe void handler3() 1082 { 1083 thirdCalled = true; 1084 assert(firstCalled); 1085 assert(secondCalled); 1086 } 1087 Signal!() onTest; 1088 onTest ~= &handler2; 1089 auto h = onTest.connectAfter(&handler2, &handler3); 1090 assert(h == &handler3); 1091 auto h2 = onTest.connectBefore(&handler2, &handler1); 1092 assert(h2 == &handler1); 1093 onTest(); 1094 assert(firstCalled && secondCalled && thirdCalled); 1095 firstCalled = secondCalled = thirdCalled = false; 1096 onTest.disconnect(h); 1097 onTest.disconnect(h2); 1098 onTest.disconnect(&handler2); 1099 onTest ~= &handler1; 1100 onTest ~= &handler3; 1101 onTest.connectBefore(&handler3, &handler2); 1102 onTest(); 1103 assert(firstCalled && secondCalled && thirdCalled); 1104 } 1105 1106 unittest 1107 { 1108 @safe void handler() {}; 1109 Signal!() onTest; 1110 assert(onTest.calculateLength() == 0); 1111 onTest.connect(&handler); 1112 assert(onTest.calculateLength() == 1); 1113 onTest.clear(); 1114 assert(onTest.calculateLength() == 0); 1115 onTest(); 1116 }