programing

일반적인 스테이트 머신의 구현 패턴이 있습니까?

goodcopy 2022. 7. 5. 23:07
반응형

일반적인 스테이트 머신의 구현 패턴이 있습니까?

C에 간단한 스테이트 머신을 실장할 필요가 있습니다.
표준 스위치 스테이트먼트가 최선의 방법입니까?
현재 상태(상태)와 이행의 트리거가 있습니다.


switch(state)
{
  case STATE_1:
     state = DoState1(transition);
     break;
  case STATE_2:
     state = DoState2(transition);
     break;
}
...
DoState2(int transition)
{
   // Do State Work
   ...
   if(transition == FROM_STATE_2) {
     // New state when doing STATE 2 -> STATE 2
   }
   if(transition == FROM_STATE_1) {
    // New State when moving STATE 1 -> STATE 2
   }
   return new_state;
}

단순한 스테이트 머신을 위한 더 나은 방법은 없을까?

EDIT: C++의 경우 Boost Statechart 라이브러리를 사용하는 것이 좋다고 생각합니다.단, C에서는 도움이 되지 않습니다.C의 사용 사례에 집중합니다.

대부분의 스테이트 머신에서는 테이블 구동 방식을 사용하는 것을 선호합니다.

typedef enum { STATE_INITIAL, STATE_FOO, STATE_BAR, NUM_STATES } state_t;
typedef struct instance_data instance_data_t;
typedef state_t state_func_t( instance_data_t *data );

state_t do_state_initial( instance_data_t *data );
state_t do_state_foo( instance_data_t *data );
state_t do_state_bar( instance_data_t *data );

state_func_t* const state_table[ NUM_STATES ] = {
    do_state_initial, do_state_foo, do_state_bar
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    return state_table[ cur_state ]( data );
};

int main( void ) {
    state_t cur_state = STATE_INITIAL;
    instance_data_t data;

    while ( 1 ) {
        cur_state = run_state( cur_state, &data );

        // do other program logic, run other state machines, etc
    }
}

물론, 이것은 복수의 스테이트 머신등을 서포트하도록 확장할 수 있습니다.이행 액션은 다음과 같이 대응할 수도 있습니다.

typedef void transition_func_t( instance_data_t *data );

void do_initial_to_foo( instance_data_t *data );
void do_foo_to_bar( instance_data_t *data );
void do_bar_to_initial( instance_data_t *data );
void do_bar_to_foo( instance_data_t *data );
void do_bar_to_bar( instance_data_t *data );

transition_func_t * const transition_table[ NUM_STATES ][ NUM_STATES ] = {
    { NULL,              do_initial_to_foo, NULL },
    { NULL,              NULL,              do_foo_to_bar },
    { do_bar_to_initial, do_bar_to_foo,     do_bar_to_bar }
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    state_t new_state = state_table[ cur_state ]( data );
    transition_func_t *transition =
               transition_table[ cur_state ][ new_state ];

    if ( transition ) {
        transition( data );
    }

    return new_state;
};

테이블 구동 접근방식은 유지 및 확장이 용이하며 상태 다이어그램에 매핑하는 것이 간단합니다.

Martin Fowler의 UML Deflusted에서 는 10장 상태 기계 다이어그램(강조 광산)에 (말장난 의도 없음)이라고 명시하고 있다.

스테이트 다이어그램은 네스트된 스위치, 스테이트 패턴 및 스테이트테이블의 3가지 주요 방법으로 실장할 수 있습니다.

휴대 전화의 디스플레이 상태의 간단한 예를 사용해 보겠습니다.

여기에 이미지 설명 입력

네스트 스위치

Fowler는 C# 코드의 예를 제시했지만, 나는 그것을 내 예에 맞게 수정했다.

public void HandleEvent(PhoneEvent anEvent) {
    switch (CurrentState) {
    case PhoneState.ScreenOff:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            if (powerLow) { // guard condition
                DisplayLowPowerMessage(); // action
                // CurrentState = PhoneState.ScreenOff;
            } else {
                CurrentState = PhoneState.ScreenOn;
            }
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenOn:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            CurrentState = PhoneState.ScreenOff;
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenCharging:
        switch (anEvent) {
        case PhoneEvent.UnplugPower:
            CurrentState = PhoneState.ScreenOff;
            break;
        }
        break;
    }
}

상태 패턴

다음은 GoF State 패턴을 사용한 예시의 구현입니다.

여기에 이미지 설명 입력

스테이트 테이블

Fowler에서 영감을 얻어 다음 표에 예를 제시하겠습니다.

송신원 스테이트타깃 스테이트이벤트 가드액션--------------------------------------------------------------------------------------ScreenOff ScreenOffButton powerLow displayLow PowerMessage를 누릅니다.ScreenOff ScreenOn 버튼 !powerLow를 누릅니다.ScreenOn ScreenOff 버튼 누르기스크린오프 스크린충전 플러그전원ScreenOn 화면충전 플러그전원화면충전 화면끄기 전원 뽑기

비교

네스트된 스위치는 모든 로직을 1개의 스폿으로 유지하지만 많은 상태 및 천이가 있을 경우 코드를 읽기 어려울 수 있습니다.다른 접근법보다 안전하고 검증이 쉬울 수 있습니다(다형성이나 해석은 필요 없습니다.

상태 패턴 구현은 잠재적으로 로직을 여러 개의 개별 클래스로 분산시켜 전체적으로 이해하는 데 문제가 될 수 있습니다.반면, 소규모 수업은 따로 이해하기 쉽다.계층 내 메서드이기 때문에 코드에 많은 변경이 있을 수 있기 때문에 트랜지션을 추가하거나 삭제하여 동작을 변경하는 경우 설계는 특히 취약합니다.스몰 인터페이스의 설계 원리로 생활하는 경우, 이 패턴은 그다지 효과가 없습니다.그러나 스테이트 머신이 안정되어 있으면, 그러한 변경은 불필요합니다.

스테이트 테이블의 어프로치에서는, 컨텐츠의 통역을 작성할 필요가 있습니다(사용하고 있는 언어에 숙고하고 있는 것이 보다 쉬울 수 있습니다).이것은, 사전에 많은 작업이 필요하게 될 가능성이 있습니다.Fowler가 지적한 바와 같이 테이블이 코드와 분리되어 있으면 재컴파일하지 않고 소프트웨어의 동작을 변경할 수 있습니다.다만, 이것은 시큐러티상의 영향이 있습니다.소프트웨어는 외부 파일의 내용에 근거해 동작하고 있습니다.

편집(C 언어에는 해당되지 않음)

또, 유창한 인터페이스(내부 도메인 고유의 언어)의 어프로치도 있습니다.이것은, 1등급의 기능을 가지는 언어에 의해서 용이하게 됩니다.상태 비저장 라이브러리가 존재하며 해당 블로그는 코드와 함께 간단한 예를 보여줍니다.Java 구현(Java8 이전)에 대해 설명합니다.GitHub에서도 Python의 예를 볼 수 있었습니다.

스테이트 머신이 커짐에 따라 유지보수가 용이한 논리 그리드도 있습니다.

FSM에 대해 언급한 다른 C 질문에 대한 답변을 보신 적이 있을 것입니다.

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

다음 매크로가 정의되어 있는 경우

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

이것은, 특정의 케이스에 맞추어 수정할 수 있습니다.를 들어, 파일 "이렇게 하다"가 있을 수 .FSMFILE다음 문자를 읽는 액션을 매크로 자체에 포함시킬 수 있도록 FSM을 구동합니다.

#define FSM
#define STATE(x)         s_##x : FSMCHR = fgetc(FSMFILE); sn_##x :
#define NEXTSTATE(x)     goto s_##x
#define NEXTSTATE_NR(x)  goto sn_##x

이제 두 가지 유형의 전환이 있습니다. 하나는 상태로 이동하여 새 문자를 읽는 것이고, 다른 하나는 입력을 소비하지 않고 상태로 가는 것입니다.

또한 다음과 같은 방법으로 EOF 처리를 자동화할 수 있습니다.

#define STATE(x)  s_##x  : if ((FSMCHR = fgetc(FSMFILE) == EOF)\
                             goto sx_endfsm;\
                  sn_##x :

#define ENDFSM    sx_endfsm:

이 방법의 장점은 자신이 그린 상태도를 작업 코드로 직접 변환할 수 있고 반대로 코드에서 상태도를 쉽게 그릴 수 있다는 것입니다.

위한 동시에 switch...)에되어 변수 값를 들어 FSM)에 됩니다.statevariable)및 이 을 복잡한일 수 .

저는 이 기술을 위대한 "컴퓨터 언어" 잡지에 실린 기사로부터 배웠습니다만, 안타깝게도 더 이상 출판되지 않습니다.

저도 테이블 어프로치를 사용했습니다.하지만 오버헤드가 있습니다.두 번째 포인터 목록을 저장하는 이유는 무엇입니까?()이 없는 C의 함수는 상수 포인터입니다.다음과 같은 작업을 수행할 수 있습니다.

struct state;
typedef void (*state_func_t)( struct state* );

typedef struct state
{
  state_func_t function;

  // other stateful data

} state_t;

void do_state_initial( state_t* );
void do_state_foo( state_t* );
void do_state_bar( state_t* );

void run_state( state_t* i ) {
    i->function(i);
};

int main( void ) {
    state_t state = { do_state_initial };

    while ( 1 ) {
        run_state( state );

        // do other program logic, run other state machines, etc
    }
}

물론 두려움 요인(안전 대 속도)에 따라 유효한 포인터가 있는지 확인하는 것이 좋습니다.스테이트 머신이 3개 이상일 경우 위의 접근법은 동등한 스위치 또는 테이블 접근법보다 적은 명령이어야 합니다.다음과 같이 매크로화할 수도 있습니다.

#define RUN_STATE(state_ptr_) ((state_ptr_)->function(state_ptr_))

또, OP의 예에서는, 스테이트 머신을 생각하고 설계할 때는, 심플화를 실시할 필요가 있다고 느끼고 있습니다.나는 전환 상태가 논리에 사용되어야 한다고 생각하지 않는다.각 상태 함수는 과거 상태에 대한 명시적인 지식 없이 주어진 역할을 수행할 수 있어야 합니다.기본적으로 현재 상태에서 다른 상태로 이행하는 방법을 설계합니다.

마지막으로, "기능적" 경계를 기반으로 상태 시스템의 설계를 시작하지 말고 하위 함수를 사용하십시오.대신, 속행하기 전에 어떤 일이 일어날 때까지 기다려야 하는 시간에 따라 주를 나눕니다.이렇게 하면 결과를 얻기 전에 상태 시스템을 실행해야 하는 횟수를 최소화할 수 있습니다.이는 I/O 함수 또는 인터럽트 핸들러를 쓸 때 중요합니다.

또, classic switch 스테이트먼트의 장점과 단점도 몇 가지 있습니다.

장점:

  • 그것은 언어로 되어 있기 때문에 문서화되어 있고 명확합니다.
  • 상태가 정의되어 있습니다.
  • 1개의 함수 호출로 여러 상태를 실행할 수 있습니다.
  • 모든 상태에 공통되는 코드는 스위치문 전후로 실행할 수 있습니다.

단점:

  • 1개의 함수 호출로 여러 상태를 실행할 수 있습니다.
  • 모든 상태에 공통되는 코드는 스위치문 전후로 실행할 수 있습니다.
  • 스위치의 실장이 늦어질 수 있다

pro와 con의 2가지 Atribute에 주의해 주세요.나는 스위치로 인해 국가 간에 너무 많은 공유의 기회가 주어지고 국가 간의 상호의존성이 관리 불가능해질 수 있다고 생각한다.그러나 소수의 주에서는 가장 읽기 쉽고 유지보수가 용이할 수 있습니다.

단순한 스테이트 머신의 경우 스테이트에 switch 스테이트먼트와 Enum 타입을 사용합니다.입력에 따라 switch 스테이트먼트 내에서 이행합니다.실제 프로그램에서는 전환점을 확인하기 위해 "if(입력)"를 변경해야 합니다.이게 도움이 됐으면 좋겠다.

typedef enum
{
    STATE_1 = 0,
    STATE_2,
    STATE_3
} my_state_t;

my_state_t state = STATE_1;

void foo(char input)
{
    ...
    switch(state)
    {
        case STATE_1:
            if(input)
                state = STATE_2;
            break;
        case STATE_2:
            if(input)
                state = STATE_3;
            else
                state = STATE_1;
            break;
        case STATE_3:
            ...
            break;
    }
    ...
}

c. https://github.com/kiishor/UML-State-Machine-in-C 에서 미니멀리스트 UML 스테이트 머신 프레임워크를 사용할 수 있습니다.

유한 상태 머신과 계층 상태 머신을 모두 지원합니다.API는 3개, 구조체는 2개, 열거형은 1개뿐입니다.

은 the이음 음음음 음음음 음음 the the the the the the the the the the the 。state_machine_t 할 수 입니다.상태 시스템을 생성하기 위해 상속할 수 있는 추상 구조입니다.

//! Abstract state machine structure
struct state_machine_t
{
   uint32_t Event;          //!< Pending Event for state machine
   const state_t* State;    //!< State of state machine.
};

됩니다.state_t조화됩됩니니다

되어 있는 는, 「」를 참조해 주세요.state_t 포함, 포함,

typedef struct finite_state_t state_t;

// finite state structure
typedef struct finite_state_t{
  state_handler Handler;        //!< State handler function (function pointer)
  state_handler Entry;          //!< Entry action for state (function pointer)
  state_handler Exit;           //!< Exit action for state (function pointer)
}finite_state_t;

는 API를 합니다.dispatch_event스테이트 머신에 이벤트를 디스패치하고 스테이트 트래버설용 API 2개를 디스패치합니다.

state_machine_result_t dispatch_event(state_machine_t* const pState_Machine[], uint32_t quantity);
state_machine_result_t switch_state(state_machine_t* const, const state_t*);

state_machine_result_t traverse_state(state_machine_t* const, const state_t*);

계층형 스테이트 머신을 구현하는 방법에 대한 자세한 내용은 GitHub 저장소를 참조하십시오.

예시
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.mdhttpsgithub.com/kiishor//blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine_enhanced/readme.mdhttpsgithub.com/kiishor/UML-State-Machine-in-C/blob/master/demo//readme.md

간단한 경우 스위치 스타일 방식을 사용할 수 있습니다.과거에 잘 작동했던 것은 전환에도 대처하는 것이었습니다.

static int current_state;    // should always hold current state -- and probably be an enum or something

void state_leave(int new_state) {
    // do processing on what it means to enter the new state
    // which might be dependent on the current state
}

void state_enter(int new_state) {
    // do processing on what is means to leave the current state
    // might be dependent on the new state

    current_state = new_state;
}

void state_process() {
    // switch statement to handle current state
}
   

Boost Library에 대해서는 아무것도 모릅니다만, 이러한 어프로치는 지극히 심플하고, 외부 의존관계도 없고, 실장도 간단합니다.

edx.org 과정 Embedded Systems - Shape the World UTAustinX - UT.6.02x, 10장 Jonathan Valvano와 Ramesh Yerrabali에서 Moore FSM을 완벽하게 구현했습니다.

struct State {
  unsigned long Out;  // 6-bit pattern to output
  unsigned long Time; // delay in 10ms units 
  unsigned long Next[4]; // next state for inputs 0,1,2,3
}; 

typedef const struct State STyp;

//this example has 4 states, defining constants/symbols using #define
#define goN   0
#define waitN 1
#define goE   2
#define waitE 3


//this is the full FSM logic coded into one large array of output values, delays, 
//and next states (indexed by values of the inputs)
STyp FSM[4]={
 {0x21,3000,{goN,waitN,goN,waitN}}, 
 {0x22, 500,{goE,goE,goE,goE}},
 {0x0C,3000,{goE,goE,waitE,waitE}},
 {0x14, 500,{goN,goN,goN,goN}}};
unsigned long currentState;  // index to the current state 

//super simple controller follows
int main(void){ volatile unsigned long delay;
//embedded micro-controller configuration omitteed [...]
  currentState = goN;  
  while(1){
    LIGHTS = FSM[currentState].Out;  // set outputs lines (from FSM table)
    SysTick_Wait10ms(FSM[currentState].Time);
    currentState = FSM[currentState].Next[INPUT_SENSORS];  
  }
}

C/C++에 실용적인 현황표라는 책이 있습니다.하지만 우리가 필요로 하는 것에 비해 너무 무겁다.

제가 좋아하는 패턴 중 하나는 스테이트 디자인 패턴입니다.동일한 입력 세트에 응답하거나 다르게 동작합니다.
스테이트 머신에 switch/case 스테이트먼트를 사용하는 경우의 문제 중 하나는 스테이트를 더 많이 작성할수록 스위치/case가 읽기/유지하기가 어려워지고 정리되지 않은 스파게티 코드가 촉진되며 중단 없이 변경하기가 점점 더 어려워진다는 것입니다.디자인 패턴을 사용하면 데이터를 더 잘 정리할 수 있습니다. 이치노어떤 상태에서 왔는지 주 코드를 설계하는 것이 아니라, 새로운 상태로 들어갔을 때 상태를 기록하도록 코드를 구성합니다.그러면 효과적으로 이전 상태를 기록할 수 있습니다.저는 @JoshPetit의 답변이 마음에 들어 GoF 책에서 직접 인용한 솔루션을 한 단계 더 발전시켰습니다.

stateCtxt.h:

#define STATE (void *)
typedef enum fsmSignal
{
   eEnter =0,
   eNormal,
   eExit
}FsmSignalT;

typedef struct fsm 
{
   FsmSignalT signal;
   // StateT is an enum that you can define any which way you want
   StateT currentState;
}FsmT;
extern int STATECTXT_Init(void);
/* optionally allow client context to set the target state */
extern STATECTXT_Set(StateT  stateID);
extern void STATECTXT_Handle(void *pvEvent);

stateCtxt.c:

#include "stateCtxt.h"
#include "statehandlers.h"

typedef STATE (*pfnStateT)(FsmSignalT signal, void *pvEvent);

static FsmT      fsm;
static pfnStateT UsbState ;

int STATECTXT_Init(void)
{    
    UsbState = State1;
    fsm.signal = eEnter;
    // use an enum for better maintainability
    fsm.currentState = '1';
    (*UsbState)( &fsm, pvEvent);
    return 0;
}

static void ChangeState( FsmT *pFsm, pfnStateT targetState )
{
    // Check to see if the state has changed
    if (targetState  != NULL)
    {
        // Call current state's exit event
        pFsm->signal = eExit;
        STATE dummyState = (*UsbState)( pFsm, pvEvent);

        // Update the State Machine structure
        UsbState = targetState ;

        // Call the new state's enter event
        pFsm->signal = eEnter;            
        dummyState = (*UsbState)( pFsm, pvEvent);
    }
}

void STATECTXT_Handle(void *pvEvent)
{
    pfnStateT newState;

    if (UsbState != NULL)
    {
         fsm.signal = eNormal;
         newState = (*UsbState)( &fsm, pvEvent );
         ChangeState( &fsm, newState );
    }        
}


void STATECTXT_Set(StateT  stateID)
{
     prevState = UsbState;
     switch (stateID) 
     {
         case '1':               
            ChangeState( State1 );
            break;
          case '2':
            ChangeState( State2);
            break;
          case '3':
            ChangeState( State3);
            break;
     }
}

국가 관리인h:

/* define state handlers */
extern STATE State1(void);
extern STATE State2(void);
extern STATE State3(void);

스테이트 핸들러.c:

#include "stateCtxt.h:"

/* Define behaviour to given set of inputs */
STATE State1(FsmT *fsm, void *pvEvent)
{   
    STATE nextState;
    /* do some state specific behaviours 
     * here
     */
    /* fsm->currentState currently contains the previous state
     * just before it gets updated, so you can implement behaviours 
     * which depend on previous state here
     */
    fsm->currentState = '1';
    /* Now, specify the next state
     * to transition to, or return null if you're still waiting for 
     * more stuff to process.  
     */
    switch (fsm->signal)
    {
        case eEnter:
            nextState = State2;
            break;
        case eNormal:
            nextState = null;
            break;
        case eExit:
            nextState = State2;
            break;
    }

    return nextState;
}

STATE  State3(FsmT *fsm, void *pvEvent)
{
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '2';
    /* Now, specify the next state
     * to transition to
     */
     return State1;
}

STATE   State2(FsmT *fsm, void *pvEvent)
{   
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '3';
    /* Now, specify the next state
     * to transition to
     */
     return State3;
}

대부분의 스테이트 머신, 특히.유한 상태 기계, 각 상태는 다음 상태가 어떤 상태여야 하는지 및 다음 상태로의 이행 기준을 알 수 있습니다.루즈 스테이트 설계에서는 그렇지 않을 수 있기 때문에 상태를 이행하기 위한 API를 표시할 수 있습니다.더 추상화해야 할 경우 각 상태 핸들러를 GoF 북의 구체적인 상태 핸들러와 동일한 자체 파일로 분리할 수 있습니다.설계가 단순하고 몇 가지 상태만 있는 경우 stateCtxt.c와 statehandlers.c를 모두 하나의 파일로 결합하여 단순화할 수 있습니다.

switch()는 C에서 스테이트 머신을 구현하는 강력하고 표준적인 방법이지만, 다수의 스테이트가 있는 경우 유지보수성이 저하될 수 있습니다.또 다른 일반적인 방법은 함수 포인터를 사용하여 다음 상태를 저장하는 것입니다.다음의 간단한 예에서는, 설정/리셋 플립 플랍을 실장하고 있습니다.

/* Implement each state as a function with the same prototype */
void state_one(int set, int reset);
void state_two(int set, int reset);

/* Store a pointer to the next state */
void (*next_state)(int set, int reset) = state_one;

/* Users should call next_state(set, reset). This could
   also be wrapped by a real function that validated input
   and dealt with output rather than calling the function
   pointer directly. */

/* State one transitions to state one if set is true */
void state_one(int set, int reset) {
    if(set)
        next_state = state_two;
}

/* State two transitions to state one if reset is true */
void state_two(int set, int reset) {
    if(reset)
        next_state = state_one;
}

Libero FSM 제너레이터 소프트웨어를 조사하는 것이 좋습니다.상태 설명 언어 및/또는 (윈도우즈) 상태 다이어그램 편집기에서 C, C++, java 및 기타 많은 코드 및 멋진 문서 및 다이어그램을 생성할 수 있습니다.iMatix의 소스 및 바이너리

이 문서는 상태 패턴에 적합한 문서입니다(구체적으로는 C가 아니라 C++이지만).

Head First Design Patterns(헤드 퍼스트 디자인 패턴)」를 손에 넣을 수 있다면, 설명과 예는 매우 명확합니다.

제 경험상 'switch' 문을 사용하는 것이 여러 상태를 처리하는 표준 방법입니다.당신이 상태별 처리로 이행값을 전달하고 있는 것이 놀랍습니다.저는 국가 기계의 요점은 각 주가 하나의 행동을 하는 것이라고 생각했습니다.다음으로 이행할 새로운 상태를 결정합니다.따라서 각 상태 처리 기능이 상태 진입을 위해 수정된 모든 작업을 즉시 수행한 후 다른 상태로 이행할 필요가 있는지 여부를 결정할 것으로 예상했습니다.

「 」를 서포트하는 .__COUNTER__심플하지만 큰 상태의 매쉬인에 사용할 수 있습니다.

  #define START 0      
  #define END 1000

  int run = 1;
  state = START;    
  while(run)
  {
    switch (state)
    {
        case __COUNTER__:
            //do something
            state++;
            break;
        case __COUNTER__:
            //do something
            if (input)
               state = END;
            else
               state++;
            break;
            .
            .
            .
        case __COUNTER__:
            //do something
            if (input)
               state = START;
            else
               state++;
            break;
        case __COUNTER__:
            //do something
            state++;
            break;
        case END:
            //do something
            run = 0;
            state = START;
            break;
        default:
            state++;
            break;
     } 
  } 

「 」를 사용하는 __COUNTER__하드 코드화된 숫자 대신 모든 상태의 번호를 다시 매기지 않고 다른 상태의 중간에 상태를 추가할 수 있습니다.가 「」를 서포트하고 않은 .__COUNTER__할 수 __LINE__

C++에서는 State 패턴을 고려합니다.

질문은 "일반적인 데이터베이스 구현 패턴이 있습니까?"와 비슷합니다.해답은 당신이 무엇을 성취하고 싶은지에 달려있다.더 큰 결정론적 상태 시스템을 구현하려면 모델 및 상태 시스템 생성기를 사용할 수 있습니다.예시는 www.StateSoft.org - SM Gallery에서 보실 수 있습니다.야누시 도브로월스키

또한 테이블 구동 방식을 선호합니다.사용한 적이 있다switch★★★★★★ 는, 있는 확인하는 것입니다.지금까지의 주된 문제는, 트랜지션을 디버깅 해, 설계한 스테이트 머신이 올바르게 실장되고 있는 것을 확인하는 것입니다.이는 상태 및 이벤트가 많은 경우에 발생했습니다.

테이블 주도 접근방식에서는 상태와 전환이 한 곳에 요약되어 있습니다.

이 방법의 데모를 다음에 나타냅니다.

/*Demo implementations of State Machines
 *
 * This demo leverages a table driven approach and function pointers
 *
 * Example state machine to be implemented
 *
 *          +-----+      Event1        +-----+      Event2        +-----+
 *    O---->|  A  +------------------->|  B  +------------------->|  C  |
 *          +-----+                    +-----+                    +-----+
 *             ^                                                     |
 *             |                       Event3                        |
 *             +-----------------------------------------------------+
 *
 * States: A, B, C
 * Events: NoEvent (not shown, holding current state), Event1, Event2, Event3
 *
 * Partly leveraged the example here: http://web.archive.org/web/20160808120758/http://www.gedan.net/2009/03/18/finite-state-machine-matrix-style-c-implementation-function-pointers-addon/
 *
 * This sample code can be compiled and run using GCC.
 * >> gcc -o demo_state_machine demo_state_machine.c
 * >> ./demo_state_machine
 */

#include <stdio.h>
#include <assert.h>

// Definitions of state id's, event id's, and function pointer
#define N_STATES  3
#define N_EVENTS  4

typedef enum {
  STATE_A,
  STATE_B,
  STATE_C,
} StateId;

typedef enum {
  NOEVENT,
  EVENT1,
  EVENT2,
  EVENT3,
} Event;
typedef void (*StateRoutine)();

// Assert on number of states and events defined
static_assert(STATE_C==N_STATES-1,
  "Number of states does not match defined number of states");
static_assert(EVENT3==N_EVENTS-1,
  "Number of events does not match defined number of events");

// Defining State, holds both state id and state routine
typedef struct {
    StateId id;
    StateRoutine routine;
}  State;

// General functions
void evaluate_state(Event e);

// State routines to be executed at each state
void state_routine_a(void);
void state_routine_b(void);
void state_routine_c(void);

// Defining each state with associated state routine
const State state_a = {STATE_A, state_routine_a};
const State state_b = {STATE_B, state_routine_b};
const State state_c = {STATE_C, state_routine_c};

// Defning state transition matrix as visualized in the header (events not
// defined, result in mainting the same state)
State state_transition_mat[N_STATES][N_EVENTS] = {
   { state_a, state_b, state_a, state_a},
   { state_b, state_b, state_c, state_b},
   { state_c, state_c, state_c, state_a}};

// Define current state and initialize
State current_state = state_a;

int main()
{
    while(1) {
    // Event to receive from user
    int ev;

    printf("----------------\n");
    printf("Current state: %c\n", current_state.id + 65);
    printf("Event to occur: ");
    // Receive event from user
    scanf("%u", &ev);
    evaluate_state((Event) ev); // typecast to event enumeration type
    printf("-----------------\n");
    };
    return (0);
}

/*
 * Determine state based on event and perform state routine
 */
void evaluate_state(Event ev)
{
    //Determine state based on event
  current_state = state_transition_mat[current_state.id][ev];
  printf("Transitioned to state: %c\n", current_state.id + 65);
    // Run state routine
    (*current_state.routine)();
}

/*
 * State routines
 */
void state_routine_a() {
  printf("State A routine ran. \n");

}
void state_routine_b() {
  printf("State B routine ran. \n");
}
void state_routine_c() {
  printf("State C routine ran. \n");
}

부스트에는 상태 차트 라이브러리가 있습니다.http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html

하지만 그 사용법에 대해서는 말할 수는 없어요.직접 사용하지 않음(아직)

언급URL : https://stackoverflow.com/questions/133214/is-there-a-typical-state-machine-implementation-pattern

반응형