init(); // a top level initial transition dispatch(); // dispatch an event to the state machine tran(); // make a state change
enum Signal // enumeration for CParser signals { CHAR_SIG, STAR_SIG, SLASH_SIG }; enum State // enumeration for CParser states { CODE, SLASH, COMMENT, STAR }; class CParser1 { private: State myState; // the scalar state-variable long myCommentCtr; // comment character counter /* ... */ // other CParser1 attributes public: void init() { myCommentCtr = 0; tran(CODE); // default transiton } void dispatch(Signal sig); void tran(State target) { myState = target; } long getCommentCtr() const { return myCommentCtr; } };
void CParser1::dispatch(Signal sig) { switch (myState) { case CODE: switch (sig) { case SLASH_SIG: tran(SLASH); // transition to SLASH break; } break; case SLASH: switch (sig) { case STAR_SIG: myCommentCtr += 2; // SLASH-STAR count as comment tran(COMMENT); // transition to COMMENT break; case CHAR_SIG: case SLASH_SIG: tran(CODE); // go back to CODE break; } break; case COMMENT: switch (sig) { case STAR_SIG: tran(STAR); // transition to STAR break; case CHAR_SIG: case SLASH_SIG: ++myCommentCtr; // count the comment char break; } break; case STAR: switch (sig) { case STAR_SIG: ++myCommentCtr; // count STAR as comment break; case SLASH_SIG: myCommentCtr += 2; // count STAR-SLASH as comment tran(CODE); // transition to CODE break; case CHAR_SIG: myCommentCtr += 2; // count STAR-? as comment tran(COMMENT); // go back to COMMENT break; } break; } }
C Comment Parser State Transition Table (FSM Model) | |||
Events | |||
States | CHAR_SIG | STAR_SIG | SLASH_SIG |
CODE | doNothing(); tran(SLASH); |
||
SLASH | doNothing(); tran(CODE); |
a2(); tran(COMMENT); |
doNothing(); tran(CODE); |
COMMENT | a1(); tran(COMMENT)); |
doNothing(); tran(STAR); |
a1(); tran(COMMENT); |
STAR | a2(); tran(COMMENT); |
a1(); tran(STAR); |
a2(); tran(CODE); |
// generic "event processor" ... class StateTable { public: typedef void (StateTable::*Action)(); // defines type Action as pointer-to-method in StateTable struct Tran // defines table element as pair<pointer-to-member, unsigned> { Action action; unsigned nextState; }; StateTable(Tran const *table, unsigned nStates, unsigned nSignals) : myTable(table), myNsignals(nSignals), myNstates(nStates) myTable contains address of 2-d table of Tran supplied by client {} virtual ~StateTable() // virtual xctor {} void dispatch(unsigned sig) { register Tran const * t = myTable + myState*myNsignals + sig; // table lookup: Tran * t = myTable[myState,sig]; (this->*(t->action))(); // call action sequence myState = t->nextState; // "tran()" } void doNothing() {} protected: unsigned myState; private: Tran const *myTable; unsigned myNsignals; unsigned myNstates; }; // specific Comment Parser state machine ... enum Event // enumeration for CParser events { CHAR_SIG, STAR_SIG, SLASH_SIG, MAX_SIG }; enum State // enumeration for CParser states { CODE, SLASH, COMMENT, STAR, MAX_STATE }; class CParser2 : public StateTable // CParser2 state machine { public: CParser2() : StateTable(&myTable[0][0], MAX_STATE, MAX_SIG) {} void init() // initial transition { myCommentCtr = 0; myState = CODE; } long getCommentCtr() const { return myCommentCtr; } private: void a1() // action method { myCommentCtr += 1; } void a2() // action method { myCommentCtr += 2; } static StateTable::Tran const myTable[MAX_STATE][MAX_SIG]; long myCommentCtr; // comment character counter }; // initialize the "table", a 2-D array of unsigned int ("romable") StateTable::Tran const CParser2::myTable[MAX_STATE][MAX_SIG] = { { {&StateTable::doNothing, CODE }, {&StateTable::doNothing, CODE }, {&StateTable::doNothing, SLASH} }, { {&StateTable::doNothing, CODE }, {static_cast<StateTable::Action>(&CParser2::a2), COMMENT }, {&StateTable::doNothing, CODE } }, { {static_cast<StateTable::Action>(&CParser2::a1), COMMENT }, {&StateTable::doNothing,STAR }, {static_cast<StateTable::Action>(&CParser2::a1), COMMENT } }, { {static_cast<StateTable::Action>(&CParser2::a2), COMMENT }, {static_cast<StateTable::Action>(&CParser2::a1), STAR }, {static_cast<StateTable::Action>(&CParser2::a2), CODE } } };
class CParser3; // Context class class CParserState // abstract State // not abstract class: defines inheritable "doNothing" actions { public: virtual void onCHAR(CParser3 *context, char ch) {} virtual void onSTAR(CParser3 *context) {} virtual void onSLASH(CParser3 *context) {} }; class CodeState : public CParserState // concrete State "Code" { public: virtual void onSLASH(CParser3 *context); }; class SlashState : public CParserState // concrete State "Slash" { public: virtual void onCHAR(CParser3 *context, char ch); virtual void onSTAR(CParser3 *context); }; class CommentState : public CParserState //concrete State "Comment" { public: virtual void onCHAR(CParser3 *context, char ch); virtual void onSTAR(CParser3 *context); virtual void onSLASH(CParser3 *context); }; class StarState : public CParserState // concrete State "Star" { public: virtual void onCHAR(CParser3 *context, char ch); virtual void onSTAR(CParser3 *context); virtual void onSLASH(CParser3 *context); }; class CParser3 // Context class { friend class CodeState; friend class SlashState; friend class CommentState; friend class StarState; private: static CodeState myCodeState; static SlashState mySlashState; static CommentState myCommentState; static StarState myStarState; CParserState *myState; long myCommentCtr; public: void init() { myCommentCtr = 0; tran(&myCodeState); } void tran(CParserState *target) { myState = target; } long getCommentCtr() const { return myCommentCtr; } void onCHAR(char ch) { myState->onCHAR(this, ch); } void onSTAR() { myState->onSTAR(this); } void onSLASH() { myState->onSLASH(this); } };
CodeState CParser3::myCodeState; SlashState CParser3::mySlashState; CommentState CParser3::myCommentState; StarState CParser3::myStarState; void CodeState::onSLASH(CParser3 *context) { context->tran(&CParser3::mySlashState); } void SlashState::onCHAR(CParser3 *context, char ch) { context->tran(&CParser3::myCodeState); } void SlashState::onSTAR(CParser3 *context) { context->myCommentCtr += 2; context->tran(&CParser3::myCommentState); } void CommentState::onCHAR(CParser3 *context, char c) { context->myCommentCtr++; } void CommentState::onSTAR(CParser3 *context) { context->tran(&CParser3::myStarState); } void CommentState::onSLASH(CParser3 *context) { context->myCommentCtr++; } void StarState::onCHAR(CParser3 *context, char ch) { context->myCommentCtr += 2; context->tran(&CParser3::myCommentState); } void StarState::onSTAR(CParser3 *context) { context->myCommentCtr++; } void StarState::onSLASH(CParser3 *context) { context->myCommentCtr += 2; context->tran(&CParser3::myCodeState); }
Design Pattern
|
![]() |
Simplified Design Pattern
|
![]() |
|
![]() |
class Fsm { public: typedef void // return value (Fsm::* // class the function pointer is a member of State) // pointer-to-member name (unsigned sig); // argument list Fsm(State initial) : myState(initial) // ctor {} virtual ~Fsm() // virtual xtor {} void init() // initial transition { (this->*myState)(0); } void dispatch(unsigned sig) { (this->*myState)(sig); } protected: void tran(State target) { myState = target; } #define TRAN(target_) tran(static_cast<State>(target_)) State myState; }; enum Event // enumeration for CParser events { CHAR_SIG, STAR_SIG, SLASH_SIG }; class CParser4 : public Fsm { public: CParser4() : Fsm((State)initial) {} // ctor long getCommentCtr() const { return myCommentCtr; } private: long myCommentCtr; // comment character counter void initial(unsigned); // state-handler void code(unsigned sig); // state-handler void slash(unsigned sig); // state-handler void comment(unsigned sig); // state-handler void star(unsigned sig); // state-handler };
void CParser4::initial(unsigned) // initial pseudostate { myCommentCtr = 0; TRAN(&CParser4::code); // take the default transition } void CParser4::code(unsigned sig) { switch (sig) { case SLASH_SIG: TRAN(&CParser4::slash); // transition to "slash" break; } } void CParser4::slash(unsigned sig) { switch (sig) { case STAR_SIG: myCommentCtr += 2; // SLASH-STAR characters count as comment TRAN(&CParser4::comment); // transition to "comment" break; case CHAR_SIG: TRAN(&CParser4::code); // go back to "code" break; } } void CParser4::comment(unsigned sig) { switch (sig) { case STAR_SIG: TRAN(&CParser4::star); // transition to "star" break; case CHAR_SIG: case SLASH_SIG: ++myCommentCtr; // count the comment character break; } } void CParser4::star(unsigned sig) { switch (sig) { case STAR_SIG: ++myCommentCtr; // count '*' as comment character break; case CHAR_SIG: myCommentCtr += 2; // count STAR-? as comment TRAN(&CParser4::comment); // go back to "comment" break; case SLASH_SIG: myCommentCtr += 2; // count STAR-SLASH as comment TRAN(&CParser4::code); // transition to "code" break; } }
|
![]() |
|
if (guard()) { action(); }
if (guard1()) { action1(); } else if (guard2()) { action2(); } // optional default case: else { actionN(); }Note the control over action order when guards are not mutually exclusive.
if (dynamic_condition()) { if (guard1()) { action1(); } else if (guard2()) { action2(); } // optional default case: else { actionN(); } }
enum { INIT_SIG = 1, EXIT_SIG, ENTRY_SIG, USER_SIG }
enum mySignals { SIG1 = USER_SIG, SIG2, SIG3, ... }
// old void tran(State target) { myState = target; } // new void tran(State target) { (this -> *myState)(EXIT_SIG); myState = target; (this -> *myState)(ENTRY_SIG); }
void CParser4a::slash(unsigned sig) { switch (sig) { case ENTRY_SIG: // entry_action break; case EXIT_SIG: // exit_action break; case STAR_SIG: myCommentCtr += 2; // SLASH-STAR characters count as comment TRAN(&CParser4::comment); // transition to "comment" break; case CHAR_SIG: TRAN(&CParser4::code); // go back to "code" break; } }