1 /******************************************************************************* 2 * Core module for state transion 3 * 4 * Copyright: © 2019, SHOO 5 * License: [BSL-1.0](http://boost.org/LICENSE_1_0.txt). 6 * Author: SHOO 7 */ 8 module cushion.core; 9 10 11 import std.traits, std.range, std.meta, std.container; 12 import cushion.handler; 13 import cushion._internal.misc; 14 15 16 private template isStraight(int start, Em...) 17 { 18 static if (Em.length == 1) 19 { 20 enum isStraight = Em[0] == start; 21 } 22 else 23 { 24 enum isStraight = Em[0] == start && isStraight!(start+1, Em[1..$]); 25 } 26 } 27 28 29 private template isStraightEnum(E) 30 if (is(E == enum)) 31 { 32 enum isStraightEnum = (EnumMembers!(E)[0] == cast(E)0) &&isStraight!(EnumMembers!(E)[0], EnumMembers!E); 33 } 34 35 36 37 /******************************************************************************* 38 * Judge StateTransitor's Event 39 */ 40 template isEvent(Event) 41 { 42 enum bool isEvent = isStraightEnum!Event; 43 } 44 45 /******************************************************************************* 46 * Judge StateTransitor's State 47 */ 48 template isState(State) 49 { 50 enum bool isState = isStraightEnum!State; 51 } 52 53 /******************************************************************************* 54 * Judge StateTransitor's ProcHandler 55 */ 56 template isProcessHandler(Handler) 57 { 58 static if (isHandler!Handler 59 && is(HandlerReturnType!Handler == void) 60 && is(HandlerParameters!Handler == AliasSeq!())) 61 { 62 enum bool isProcessHandler = true; 63 } 64 else 65 { 66 enum bool isProcessHandler = false; 67 } 68 } 69 70 /******************************************************************************* 71 * Judge StateTransitor's ExceptionHandler 72 */ 73 template isExceptionHandler(Handler) 74 { 75 static if (isHandler!Handler 76 && is(HandlerReturnType!Handler == void) 77 && ( is(HandlerParameters!Handler == AliasSeq!(Exception)) 78 || is(HandlerParameters!Handler == AliasSeq!(Throwable)))) 79 { 80 enum bool isExceptionHandler = true; 81 } 82 else 83 { 84 enum bool isExceptionHandler = false; 85 } 86 } 87 88 /// 89 @safe unittest 90 { 91 static assert(isExceptionHandler!(void delegate(Exception)@safe[])); 92 } 93 94 95 /******************************************************************************* 96 * Judge StateTransitor's EventHandler 97 */ 98 template isEventHandler(Handler, Event) 99 { 100 static if (isHandler!Handler 101 && is(HandlerReturnType!Handler == void) 102 && is(HandlerParameters!Handler == AliasSeq!(Event))) 103 { 104 enum bool isEventHandler = true; 105 } 106 else 107 { 108 enum bool isEventHandler = false; 109 } 110 } 111 112 113 /******************************************************************************* 114 * Judge StateTransitor's EventHandler 115 */ 116 template isStateChangedHandler(Handler, State) 117 { 118 static if (isHandler!Handler 119 && is(HandlerReturnType!Handler == void) 120 && is(HandlerParameters!Handler == AliasSeq!(State, State))) 121 { 122 enum bool isStateChangedHandler = true; 123 } 124 else 125 { 126 enum bool isStateChangedHandler = false; 127 } 128 } 129 130 /// 131 @safe unittest 132 { 133 enum State {a, b, c} 134 static assert(isStateChangedHandler!(void delegate(State,State), State)); 135 } 136 137 138 139 140 private void insertBack(E)(ref SList!E list, E e) 141 { 142 list.insertAfter(list[], e); 143 } 144 145 private void insertBack(E)(ref E[] ary, E e) 146 { 147 ary ~= e; 148 } 149 private void insert(E)(ref E[] ary, E e) 150 { 151 ary ~= e; 152 } 153 private void removeFront(E)(ref E[] ary) 154 { 155 ary = ary[1..$]; 156 } 157 158 private void removeFront(E)(ref Array!E ary) 159 { 160 ary.moveAt(0); 161 ary.removeBack(); 162 } 163 164 /******************************************************************************* 165 * Judge StateTransitor's EventContainer 166 */ 167 template isEventContainer(Container) 168 { 169 static if (__traits(compiles, { 170 import std.array; 171 Container list = void; 172 auto e = list.front; 173 list.insertBack(e); 174 list.insert(e); 175 list.removeFront(); 176 if (list.empty) {} 177 })) 178 { 179 enum bool isEventContainer = true; 180 } 181 else 182 { 183 enum bool isEventContainer = false; 184 } 185 } 186 187 /// 188 @system unittest 189 { 190 enum E {a, b, c} 191 static assert(isEventContainer!(E[])); 192 static assert(isEventContainer!(SList!E)); 193 static assert(isEventContainer!(DList!E)); 194 static assert(isEventContainer!(Array!E)); 195 } 196 197 198 199 private class EventCancelException: Exception 200 { 201 this() { super(null, null, 0); } 202 } 203 204 /******************************************************************************* 205 * 206 */ 207 void cancelEvent() 208 { 209 throw new EventCancelException; 210 } 211 212 213 private class ForbiddenTransitionError: Error 214 { 215 this(string msg = null, string file = __FILE__, size_t line = __LINE__) { super(msg, file, line); } 216 } 217 218 /*************************************************************************** 219 * Get a default forbidden handler 220 */ 221 void delegate() forbiddenHandler() @safe 222 { 223 static struct Dummy 224 { 225 void forbidden() 226 { 227 throw new ForbiddenTransitionError; 228 } 229 } 230 static Dummy dummy; 231 return &dummy.forbidden; 232 } 233 234 /*************************************************************************** 235 * Get a default ignore handler 236 */ 237 void delegate() ignoreHandler() @safe 238 { 239 return null; 240 } 241 242 /*************************************************************************** 243 * Consume mode 244 */ 245 enum ConsumeMode 246 { 247 248 /*************************************************************************** 249 * Events are consumed at the same time as addition. 250 * 251 * Add an event with the put method, since the put method consumes events on 252 * the fly, we automatically call the consume method. 253 */ 254 combined, 255 256 /*************************************************************************** 257 * Events are consumed at separate timing from the of addition 258 * 259 * Need to add an event with the put method and call the consume method for 260 * consumption. 261 */ 262 separate 263 } 264 265 /******************************************************************************* 266 * StateTransitor 267 */ 268 struct StateTransitor( 269 StateType, EventType, StateType defaultStateParameter = StateType.init, 270 ProcHandler = void delegate()[], 271 ExceptionHandler = void delegate(Exception)[], 272 EventHandler = void delegate(EventType)[], 273 StateChangedHandler = void delegate(StateType newSts, StateType oldSts)[], 274 ConsumeMode consumeMode = ConsumeMode.combined, 275 EventContainer = SList!EventType) 276 { 277 static assert(isState!StateType); 278 static assert(isEvent!EventType); 279 static assert(isProcessHandler!ProcHandler); 280 static assert(isExceptionHandler!ExceptionHandler); 281 static assert(isEventHandler!(EventHandler, EventType)); 282 static assert(isStateChangedHandler!(StateChangedHandler, StateType)); 283 284 /*************************************************************************** 285 * State type of this StateTransitor 286 */ 287 alias State = StateType; 288 289 /*************************************************************************** 290 * State type of this StateTransitor 291 */ 292 alias Event = EventType; 293 294 /*************************************************************************** 295 * Count of kind of state in this StateTransitor 296 */ 297 enum size_t stateCount = EnumMembers!(State).length; 298 299 /*************************************************************************** 300 * Count of kind of event in this StateTransitor(Not a count of unconsumed event) 301 */ 302 enum size_t eventCount = EnumMembers!(Event).length; 303 304 /*************************************************************************** 305 * Default state of this StateTransitor 306 */ 307 enum State defaultState = defaultStateParameter; 308 309 /*************************************************************************** 310 * Cell of table 311 */ 312 static struct Cell 313 { 314 /// next state 315 State nextState = defaultState; 316 /// handler 317 ProcHandler handler; 318 /// Constructor 319 pragma(inline) this(State s, ProcHandler h) 320 { 321 nextState = s; 322 handler = h; 323 } 324 /// ditto 325 pragma(inline) this(Func)(State s, Func h) 326 if (isHandlerAddable!(ProcHandler, Func)) 327 { 328 nextState = s; 329 cushion.handler.set(handler, h); 330 } 331 } 332 private: 333 Cell[stateCount][eventCount] _table; 334 State _currentState = defaultState; 335 string _matrixName; 336 string[stateCount] _stateNames; 337 string[eventCount] _eventNames; 338 ExceptionHandler _exceptionHandler; 339 EventHandler _eventHandler; 340 StateChangedHandler _stateChangedHandler; 341 EventContainer _events; 342 343 public: 344 345 /*************************************************************************** 346 * Constractor 347 */ 348 pragma(inline) this(Cell[stateCount][eventCount] tbl) 349 { 350 initialize(tbl); 351 } 352 353 /*************************************************************************** 354 * Initialize of table 355 */ 356 pragma(inline) void initialize(Cell[stateCount][eventCount] tbl) 357 { 358 import std.algorithm; 359 move(tbl, _table); 360 cushion.handler.clear(_exceptionHandler); 361 cushion.handler.clear(_stateChangedHandler); 362 cushion.handler.clear(_eventHandler); 363 _events.clear(); 364 } 365 366 /*************************************************************************** 367 * Get/Set matrix name 368 */ 369 void matrixName(string str) @safe @nogc nothrow pure @property 370 { 371 _matrixName = str; 372 } 373 374 /// ditto 375 string matrixName() @safe @nogc nothrow pure const @property 376 { 377 return _matrixName; 378 } 379 380 /*************************************************************************** 381 * Set/Get event names 382 */ 383 void setEventName(Event ev, string evname) @safe @nogc nothrow pure 384 { 385 assert(ev < _eventNames.length); 386 _eventNames[ev] = evname; 387 } 388 389 /// ditto 390 string getEventName(Event ev) @safe @nogc pure const 391 { 392 assert(ev < _eventNames.length); 393 return _eventNames[ev]; 394 } 395 396 /// ditto 397 void eventNames(in string[eventCount] names) @safe @nogc nothrow pure @property 398 { 399 _eventNames[] = names[]; 400 } 401 402 /// ditto 403 const(string)[] eventNames() @safe @nogc nothrow pure const @property 404 { 405 return _eventNames; 406 } 407 408 409 /*************************************************************************** 410 * Set/Get state names 411 */ 412 void setStateName(State st, string stname) @safe @nogc nothrow pure 413 { 414 assert(st < _stateNames.length); 415 _stateNames[st] = stname; 416 } 417 418 /// ditto 419 string getStateName(State st) @safe @nogc pure const 420 { 421 assert(st < _stateNames.length); 422 return _stateNames[st]; 423 } 424 425 /// ditto 426 void stateNames(in string[stateCount] names) @safe @nogc nothrow pure @property 427 { 428 _stateNames[] = names[]; 429 } 430 431 /// ditto 432 const(string)[] stateNames() @safe nothrow pure const @property 433 { 434 return _stateNames; 435 } 436 437 438 /*************************************************************************** 439 * Check current state 440 */ 441 State currentState() @safe @nogc nothrow pure const @property 442 { 443 return _currentState; 444 } 445 446 447 /*************************************************************************** 448 * Change current state enforcely 449 */ 450 void enforceState(State sts) @system @nogc nothrow pure 451 { 452 _currentState = sts; 453 } 454 455 456 /*************************************************************************** 457 * Set next state 458 * 459 * If the state is `s` and event `e` is consumed, the next state will be `nextState` 460 */ 461 void setNextState(State s, Event e, State nextState) 462 { 463 _table[e][s].nextState = nextState; 464 } 465 466 /*************************************************************************** 467 * Set handler 468 * 469 * If the state is `s` and event `e` is consumed, the `handler` will be called. 470 * If other handler has already been set, the other handler is no longer used and replaced by `handler` instead. 471 */ 472 void setHandler(Func)(State s, Event e, Func handler) 473 if (isHandlerAssignable!(ProcHandler, Func)) 474 { 475 cushion.handler.set(_table[e][s].handler, handler); 476 } 477 478 /*************************************************************************** 479 * Add handler 480 * 481 * If the state is `s` and event `e` is consumed, the `handler` will be called. 482 * If other handler has already been set, `handler` will be added to the other handler and executed. 483 */ 484 void addHandler(Func)(State s, Event e, Func handler) 485 if (isHandlerAddable!(ProcHandler, Func)) 486 { 487 cushion.handler.add(_table[e][s].handler, handler); 488 } 489 490 491 /*************************************************************************** 492 * Remove handler related `s` and `e`. 493 */ 494 void removeHandler(Func)(State s, Event e, Func handler) 495 if (isHandlerAddable!(ProcHandler, Func)) 496 { 497 cushion.handler.remove(_table[e][s].handler, handler); 498 } 499 500 /*************************************************************************** 501 * Remove all handler related `s` and `e`. 502 */ 503 void clearHandler(State s, Event e) 504 { 505 cushion.handler.clear(_table[e][s].handler); 506 } 507 508 509 /*************************************************************************** 510 * Set exception handler 511 * 512 * If other handler has already been set, the other handler is no longer used and replaced by `handler` instead. 513 */ 514 void setExceptionHandler(Func)(Func handler) 515 if (isHandlerAssignable!(ExceptionHandler, Func)) 516 { 517 cushion.handler.set(_exceptionHandler, handler); 518 } 519 520 /*************************************************************************** 521 * Add exception handler 522 * 523 * If other handler has already been set, `handler` will be added to the other handler and executed. 524 */ 525 void addExceptionHandler(Func)(Func handler) 526 if (isHandlerAddable!(ExceptionHandler, Func)) 527 { 528 cushion.handler.add(_exceptionHandler, handler); 529 } 530 531 /*************************************************************************** 532 * Remove exception handler. 533 */ 534 void removeExceptionHandler(Func)(Func handler) 535 if (isHandlerAddable!(ExceptionHandler, Func)) 536 { 537 cushion.handler.remove(_exceptionHandler, handler); 538 } 539 540 /*************************************************************************** 541 * Remove all exception handler. 542 */ 543 void clearExceptionHandler() 544 { 545 cushion.handler.clear(_exceptionHandler); 546 } 547 548 549 /*********************************************************************** 550 * Set event handler 551 * 552 * If other handler has already been set, the other handler is no longer used and replaced by `handler` instead. 553 */ 554 void setEventHandler(Func)(Func handler) 555 if (isHandlerAssignable!(EventHandler, Func)) 556 { 557 cushion.handler.set(_eventHandler, handler); 558 } 559 560 /*********************************************************************** 561 * Add event handler 562 * 563 * If other handler has already been set, `handler` will be added to the other handler and executed. 564 */ 565 void addEventHandler(Func)(Func handler) 566 if (isHandlerAddable!(EventHandler, Func)) 567 { 568 cushion.handler.add(_eventHandler, handler); 569 } 570 571 /*********************************************************************** 572 * Remove event handler. 573 */ 574 void removeEventHandler(Func)(Func handler) 575 if (isHandlerAddable!(EventHandler, Func)) 576 { 577 cushion.handler.remove(_eventHandler, handler); 578 } 579 580 /*********************************************************************** 581 * Remove all event handler. 582 */ 583 void clearEventHandler() 584 { 585 cushion.handler.clear(_eventHandler); 586 } 587 588 589 /*************************************************************************** 590 * Set state changed handler 591 * 592 * If other handler has already been set, the other handler is no longer used and replaced by `handler` instead. 593 */ 594 void setStateChangedHandler(Func)(Func handler) 595 if (isHandlerAssignable!(StateChangedHandler, Func)) 596 { 597 cushion.handler.set(_stateChangedHandler, handler); 598 } 599 600 /*************************************************************************** 601 * Add state changed handler 602 * 603 * If other handler has already been set, `handler` will be added to the other handler and executed. 604 */ 605 void addStateChangedHandler(Func)(Func handler) 606 if (isHandlerAddable!(StateChangedHandler, Func)) 607 { 608 cushion.handler.add(_stateChangedHandler, handler); 609 } 610 611 /*************************************************************************** 612 * Remove state changed handler. 613 */ 614 void removeStateChangedHandler(Func)(Func handler) 615 if (isHandlerAddable!(StateChangedHandler, Func)) 616 { 617 cushion.handler.remove(_stateChangedHandler, handler); 618 } 619 620 /*********************************************************************** 621 * Remove all state changed handler. 622 */ 623 void clearStateChangedHandler() 624 { 625 cushion.handler.clear(_stateChangedHandler); 626 } 627 628 /*************************************************************************** 629 * Add a event 630 */ 631 void put(Event e) @safe 632 { 633 if (!_events.empty) 634 { 635 _events.insertBack(e); 636 return; 637 } 638 else 639 { 640 _events.insert(e); 641 } 642 while (consumeMode == ConsumeMode.combined && !_events.empty) 643 consume(); 644 } 645 646 /*************************************************************************** 647 * Consume a event 648 */ 649 void consume() @safe 650 { 651 if (_events.empty) 652 return; 653 try 654 { 655 auto ev = _events.front; 656 bool cancel; 657 try 658 { 659 cushion.handler.call(_eventHandler, ev); 660 cushion.handler.call(_table[ev][_currentState].handler); 661 } 662 catch (EventCancelException e) 663 { 664 cancel = true; 665 } 666 if (!cancel) 667 { 668 auto oldstate = _currentState; 669 _currentState = _table[ev][_currentState].nextState; 670 cushion.handler.call(_stateChangedHandler, oldstate, _currentState); 671 } 672 } 673 catch (HandlerParameters!ExceptionHandler[0] e) 674 { 675 cushion.handler.call(_exceptionHandler, e); 676 } 677 // _eventsが空でなければ、必ずremoveFrontできなければならない。 678 // できないならば、それはおかしい。 679 () @trusted 680 { 681 assert(!_events.empty); 682 try 683 { 684 _events.removeFront(); 685 } 686 catch (Throwable e) 687 { 688 assert(0); 689 } 690 }(); 691 } 692 693 /*************************************************************************** 694 * Check for unconsumed events 695 */ 696 bool emptyEvents() const @property 697 { 698 return _events.empty; 699 } 700 } 701 702 /// ditto 703 template StateTransitor(alias Policy) 704 if (__traits(hasMember, Policy, "State") 705 && __traits(hasMember, Policy, "Event")) 706 { 707 alias State = Policy.State; 708 alias Event = Policy.Event; 709 alias StateTransitor = .StateTransitor!( 710 State, 711 Event, 712 getMemberValue!(Policy, "defaultStateParameter", State.init), 713 getMemberAlias!(Policy, "ProcHandler", void delegate()[]), 714 getMemberAlias!(Policy, "ExceptionHandler", void delegate(Exception)[]), 715 getMemberAlias!(Policy, "EventHandler", void delegate(Event)[]), 716 getMemberAlias!(Policy, "StateChangedHandler", void delegate(State newSts, State oldSts)[]), 717 getMemberValue!(Policy, "consumeMode", ConsumeMode.combined), 718 getMemberAlias!(Policy, "EventContainer", SList!Event)); 719 } 720 721 /// 722 @safe unittest 723 { 724 enum State { a, b } 725 enum Event { e1, e2, e3 } 726 727 alias Stm = StateTransitor!(State, Event); 728 729 alias C = Stm.Cell; 730 string msg; 731 // STM 732 auto sm = Stm([ 733 // Event StateA StateB 734 /* e1: */ [C(State.b, {msg = "a-1";}), C(State.b, forbiddenHandler)], 735 /* e2: */ [C(State.a, ignoreHandler), C(State.a, {msg = "b-2";})], 736 /* e3: */ [C(State.a, {msg = "a-3";}), C(State.a, forbiddenHandler)] 737 ]); 738 static assert(isOutputRange!(typeof(sm), Event)); 739 740 assert(sm.currentState == State.a); 741 std.range.put(sm, Event.e1); 742 assert(sm.currentState == State.b); 743 assert(msg == "a-1"); 744 sm.put(Event.e2); 745 assert(sm.currentState == State.a); 746 assert(msg == "b-2"); 747 sm.put(Event.e3); 748 assert(sm.currentState == State.a); 749 assert(msg == "a-3"); 750 sm.put(Event.e2); 751 assert(sm.currentState == State.a); 752 assert(msg == "a-3"); 753 sm.put(Event.e1); 754 assert(sm.currentState == State.b); 755 assert(msg == "a-1"); 756 } 757 758 /// 759 @safe unittest 760 { 761 struct Policy 762 { 763 enum State { a, b } 764 enum Event { e1, e2, e3 } 765 enum consumeMode = ConsumeMode.separate; 766 } 767 alias Stm = StateTransitor!Policy; 768 alias State = Policy.State; 769 alias Event = Policy.Event; 770 alias C = Stm.Cell; 771 string[] msg; 772 State[] statesOnEvent; 773 // STM 774 auto sm = Stm([ 775 // Event StateA StateB 776 /* e1: */ [C(State.b, {msg ~= "a-1";}), C(State.b, forbiddenHandler)], 777 /* e2: */ [C(State.a, ignoreHandler), C(State.a, {msg ~= "b-2";})], 778 /* e3: */ [C(State.a, {msg ~= "a-3";}), C(State.a, forbiddenHandler)] 779 ]); 780 sm.addEventHandler((Event e) 781 { 782 statesOnEvent ~= sm.currentState; 783 }); 784 static assert(isOutputRange!(typeof(sm), Event)); 785 786 assert(sm.currentState == State.a); 787 put(sm, [Event.e1, Event.e2, Event.e3, Event.e2, Event.e1]); 788 assert(sm.currentState == State.a); 789 790 while (!sm.emptyEvents) 791 sm.consume(); 792 with (State) 793 assert(statesOnEvent == [a, b, a, a, a]); 794 assert(sm.currentState == State.b); 795 assert(msg == ["a-1", "b-2", "a-3", "a-1"]); 796 } 797 798 799 @safe unittest 800 { 801 enum S { sa, sb } 802 enum E { ea, eb, ec } 803 StateTransitor!(S, E) st; 804 int x; 805 void inc() { x++; } 806 void inc2() { x++;x++; } 807 void dec() { x++; } 808 void ex(Exception e) {} 809 void ex2(Exception e) {} 810 void ev(E e) {} 811 void ev2(E e) {} 812 void sch(S a, S b) {} 813 void sch2(S a, S b) {} 814 st.setHandler(S.sa, E.ea, &inc); 815 st.addHandler(S.sa, E.ea, &inc2); 816 st.addHandler(S.sa, E.eb, &dec); 817 st.setNextState(S.sa, E.eb, S.sb); 818 st.removeHandler(S.sa, E.ea, &inc); 819 st.clearHandler(S.sa, E.ea); 820 821 st.setExceptionHandler(&ex); 822 st.addExceptionHandler(&ex2); 823 st.removeExceptionHandler(&ex); 824 st.clearExceptionHandler(); 825 826 st.setEventHandler(&ev); 827 st.addEventHandler(&ev2); 828 st.removeEventHandler(&ev); 829 st.clearEventHandler(); 830 831 st.setStateChangedHandler(&sch); 832 st.addStateChangedHandler(&sch2); 833 st.removeStateChangedHandler(&sch); 834 st.clearStateChangedHandler(); 835 } 836 837 @safe unittest 838 { 839 enum State { stop, play, pause } 840 enum Event { onStart, onStop } 841 auto stm = StateTransitor!(State, Event)(); 842 string txt; 843 stm.addHandler(State.stop, Event.onStart, (){ txt = "play"; }); 844 stm.addHandler(State.play, Event.onStart, (){ txt = "pause"; }); 845 stm.addHandler(State.play, Event.onStop, (){ txt = "stop"; }); 846 stm.addHandler(State.pause, Event.onStart, (){ txt = "play"; }); 847 stm.addHandler(State.pause, Event.onStop, (){ txt = "stop"; }); 848 stm.setNextState(State.stop, Event.onStart, State.play); 849 stm.setNextState(State.play, Event.onStart, State.pause); 850 stm.setNextState(State.play, Event.onStop, State.stop); 851 stm.setNextState(State.pause, Event.onStart, State.play); 852 stm.setNextState(State.pause, Event.onStop, State.stop); 853 854 assert(stm.currentState == State.stop); 855 stm.put(Event.onStart); 856 assert(txt == "play"); 857 assert(stm.currentState == State.play); 858 stm.put(Event.onStop); 859 assert(txt == "stop"); 860 assert(stm.currentState == State.stop); 861 862 } 863 864 865 866 /******************************************************************************* 867 * Default Policy of StateTransitor 868 */ 869 template CreateStateTransitorPolicy( 870 State_, Event_, State_ defaultStateParameter_ = State_.init, 871 ProcHandler_ = void delegate()[], 872 ExceptionHandler_ = void delegate(Exception)[], 873 EventHandler_ = void delegate(Event_)[], 874 StateChangedHandler_ = void delegate(State_ newSts, State_ oldSts)[], 875 ConsumeMode consumeMode_ = ConsumeMode.combined, 876 EventContainer_ = SList!Event_) 877 { 878 alias State = State_; 879 alias Event = Event_; 880 enum State_ defaultStateParameter = defaultStateParameter_; 881 alias ProcHandler = ProcHandler_; 882 alias ExceptionHandler = ExceptionHandler_; 883 alias EventHandler = EventHandler_; 884 alias StateChangedHandler = StateChangedHandler_; 885 enum ConsumeMode consumeMode = consumeMode_; 886 alias EventContainer = EventContainer_; 887 } 888 889 /// 890 @safe unittest 891 { 892 enum MyState { a } 893 enum MyEvent { a } 894 alias ST1 = StateTransitor!(MyState, MyEvent); 895 alias Policy2 = CreateStateTransitorPolicy!(MyState, MyEvent); 896 alias ST2 = StateTransitor!Policy2; 897 struct Policy3 898 { 899 alias State = MyState; 900 alias Event = MyEvent; 901 } 902 alias ST3 = StateTransitor!Policy3; 903 static assert(is(ST1 == ST2)); 904 static assert(is(ST1 == ST3)); 905 }