我们需要实现一个简单的状态机 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;
}

是否有更好的办法 对于简单的国家机

编辑:C++,我认为提高 状态 库的可能的路要走。但是,它不会 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;
};

表驱动方法更易于维护和扩展,更易于映射到状态图。

其他提示

你可能已经看到了我提到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

可以根据具体情况对其进行修改。例如,您可能有一个要驱动FSM的文件 FSMFILE ,因此您可以将读取下一个char的操作合并到宏本身中:

#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:

这种方法的好处在于,您可以直接将绘制的状态图转换为工作代码,相反,您可以轻松地从代码中绘制状态图。

在实现FSM的其他技术中,转换的结构被隐藏在控制结构中(而如果,切换......)并由变量值(通常是 state 变量)控制它可能将漂亮的图表与复杂的代码联系起来是一项复杂的任务。

我从一篇出现在伟大的“计算机语言”上的文章中学到了这种技巧。不幸的是,该杂志已不再发表。

我还有用的表的方法。然而,有的开销。为什么储存的第二个清单的指点?一个功能在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
    }
}

当然这取决于你的恐惧的因素(即安全vs速度),您可能想要检查对于有效的指针。国家机器大于三个或使国家的做法上应该小指比同等的开关或表的方法。你甚至可以宏观ize为:

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

还有,我觉得从运的例子,有一个简化,应当考虑/设计一个国家机。我没事的过渡状态应该被用于逻辑。每个国家的功能应该能够执行对其赋予的作用没有明确认识过去的状态(s)。基本上你的设计如何从过渡的状态,你在给另一国。

最后,不要开始设计一个国家机的根据"功能"边界,使用子的功能。而不是分裂的国家的基础时,你会需要等待一些事情发生之前,你可以继续。这将有助于最大限度地减少数量的时候,你已经运行的国家机器之前,你得到一个结果。这可是重要时编写I/O功能,或中断的处理程序。

此外,一些利弊的经典交换的声明:

赞成:

  • 它在语言,因此它是记录和清楚
  • 国家的定义是在那里他们被称为
  • 可以执行多个国家在一个功能呼叫
  • 代码的共同所有国家可以执行之前和之后的开关声明,

缺点:

  • 可以执行多个国家在一个功能呼叫
  • 代码的共同所有国家可以执行之前和之后的开关声明,
  • 关执行可以是缓慢的

注意到两个属性,都赞成和反对的.我认为,开关的允许的机会太过分享各国之间和相互依存关系的国家之间可以变得难以管理。然而,对于一个少数国家,这可能是最可读和易于维护。

对于一个简单的状态机,只需为您的状态使用switch语句和枚举类型。根据您的输入在switch语句中进行转换。在真实的程序中,你显然会改变“if(输入)”和“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;
    }
    ...
}

还有逻辑网格,这是随着状态机变得越来越难以维护

Martin Fowler的UML Distilled 中,他在第10章State Machine中说明(没有双关语)图(强调我的):

  

状态图可以通过三种主要方式实现:嵌套开关状态模式,以及   状态表

让我们使用手机显示状态的简化示例:

嵌套开关

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模式的示例的实现:

状态表

从福勒那里获取灵感,这是我的例子:

Source State    Target State    Event         Guard        Action
--------------------------------------------------------------------------------------
ScreenOff       ScreenOff       pressButton   powerLow     displayLowPowerMessage  
ScreenOff       ScreenOn        pressButton   !powerLow
ScreenOn        ScreenOff       pressButton
ScreenOff       ScreenCharging  plugPower
ScreenOn        ScreenCharging  plugPower
ScreenCharging  ScreenOff       unplugPower

比较

嵌套开关将所有逻辑保留在一个位置,但是当存在大量状态和转换时,代码很难读取。与其他方法相比,它可能更安全,更容易验证(无多态或解释)。

状态模式实现可能会将逻辑分散到几个单独的类中,这可能会将其理解为一个整体问题。另一方面,小班很容易理解。如果通过添加或删除转换来更改行为,则设计特别脆弱,因为它们是层次结构中的方法,并且代码可能会有很多更改。如果你遵循小接口的设计原则,你会发现这种模式并没有真正做到这一点。但是,如果状态机稳定,则不需要进行此类更改。

状态表方法需要为内容编写某种解释器(如果您使用您正在使用的语言进行反射,这可能会更容易),这可能需要做很多工作。正如Fowler指出的那样,如果您的表与您的代码分开,您可以修改软件的行为而无需重新编译。然而,这有一些安全隐患;该软件的行为基于外部文件的内容。

编辑(不是真正用于C语言)

还有一种流畅的界面(也称为内部域特定语言)方法,这可能是由具有一流的功能。存在无状态库,该博客显示了一个带代码的简单示例。讨论了 Java实现(Java8之前版本) 。我在GitHub上也看到了 Python示例

对于简单的情况,你可以使用你的开关样式方法。我发现过去运作良好的是处理转换:

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 atate
    // might be dependent on the new state

    current_state = new_state;
}

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

我对boost库一无所知,但这种方法很简单,不需要任何外部依赖,并且易于实现。

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;
}

我在edx.org课程嵌入式系统 - 形成世界UTAustinX - UT.6.02x,第10章,由Jonathan Valvano和Ramesh Yerraballi发现了一个非常光滑的C实现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];  
  }
}

您可能需要查看 libero FSM生成器软件。从状态描述语言和/或(windows)状态图编辑器,您可以为C,C ++,Java和许多其他人生成代码......还有很好的文档和图表。 来自 iMatix 的来源和二进制文件

这篇文章是一个很好的状态模式(虽然它是C ++,不是特别是C)。

如果你可以把手放在书上“ Head First Design Patterns “,解释和例子非常明确。

我最喜欢的模式之一是州设计模式。对同一组给定的输入做出响应或表现不同 使用状态机的switch / 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;
     }
}

statehandlers.h:

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

statehandlers.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 / C ++实用状态图的书。 但是,对于我们需要的东西,方式太重了。

对于支持 __ COUNTER __ 的编译器,您可以将它们用于简单(但很大)的状态mashines。

  #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 ++中,请考虑状态模式

您的问题类似于“是否存在典型的数据库实施模式”? 答案取决于你想要达到的目标?如果要实现更大的确定性状态机,可以使用模型和状态机生成器。 可以在www.StateSoft.org - SM Gallery上查看示例。 Janusz Dobrowolski

在C中,对于简单的机器,我通常最终会使用这样的东西。

事件驱动的FSM由与动作(FsmAction)相关联的状态对象(FsmState)和由当前状态和事件定义的转换(FsmEdge)描述。

它还提供存储和传递FSM和用户数据,分离FSM绑定和用户绑定信息,并允许相同FSM的多个实例(即使用相同的描述但传递不同的用户数据)。

事件由整数类型(FsmEvent)表示。实现保留负值以允许特殊的公共事件(Reset,None,Any,Empty,End)。非负面事件是用户定义的。

为简单起见,转换列在数组中,并按数组顺序尝试匹配,主要提供转换优先级。它们具有可选的保护功能。下一个状态既可以直接在转换列表中指示,也可以通过跳转功能指示,这样可以提供更大的灵活性,从而实现动态FSM行为。

在转换描述中,A NULL当前状态将匹配任何状态,并且通配符事件(Any)将匹配任何事件。无论如何,触发转换的事件的实际值将被传递给跳转和保护功能。

对于复杂的FSM,简单的边缘阵列解决方案可能变得效率太低。在这种情况下,可以使用边数组和事件条目转换为转换矩阵或状态邻接列表来实现正确的跳转函数。

状态操作意味着使用可重入函数来实现,该函数区分状态进入(Enter),状态退出(Leave)和状态(State)操作。通过这种方式,可以使用静态函数变量来封装和保存本地状态信息。

通常,状态进入和退出操作将无意义地执行,并且不返回任何新事件(无)。如果没有,则新事件被捕获并立即返回。这将有效地防止在退出当前状态时发生转换。

FSM步骤功能(fsmStep)将执行FSM的单个步骤,使用新事件触发转换,或者不执行任何事件(无)来执行当前状态的状态内操作。 step函数返回一个新的发出事件,可以处理或重新馈送到FSM;或无,空和结束,如果没有事件,分别找不到转换或达到结束状态。

#ifndef FSM_H_
#define FSM_H_

#include <stdbool.h>
#include <stdint.h>

/** FSM enum type */
typedef enum
{
    // Events and return values
    fsm_User = 0, ///< User events start with this id
    fsm_Reset = -1, ///< Reset event
    fsm_None = -2, ///< No event
    fsm_Any = -3, ///< Any event, used as a wildcard
    fsm_Empty = -4, ///< No transition found for event
    fsm_End = -5, ///< Final state event generated when FSM reaches end state, or stop processing when used in transition

    // Action types
    fsm_Enter = 0, ///< Entry action
    fsm_State, ///< In-state action
    fsm_Leave ///< Exit action
} fsm_e;

typedef int FsmEvent; ///< Type for events
typedef struct FsmState FsmState; ///< Type for states
typedef struct FsmEdge FsmEdge; ///< Type for edges (transitions)

/** State action functor
    @param state Pointer to this state
    @param type Action type (Enter/State/Leave)
    @param frto Pointer to from(Enter)/to(Leave) state, NULL otherwise
    @param data User data
    @return Event id in case of a new triggered event, fsm_None otherwise
*/
typedef FsmEvent (*FsmAction)(FsmState *state, fsm_e type, FsmState *frto, void *data);

/** FSM state object */
struct FsmState
{
    FsmAction action; ///< Per-state action
    void *data; ///< Per-state data
};

/** State jump functor
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Pointer to the next state and NULL for end state
*/
typedef FsmState *(*FsmJump)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** Guard function
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Guard result
*/
typedef bool (*FsmGuard)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** FSM edge transition */
struct FsmEdge
{
    FsmState *state; ///< Matching current state pointer, or NULL to match any state
    FsmEvent event; ///< Matching event id or fsm_Any for wildcard
    void *next; ///< Next state pointer (FsmState *) or jump function (FsmJump)
    FsmGuard guard; ///< Transition guard function
    void *data; ///< Per-edge data
};

typedef struct Fsm Fsm;
struct Fsm
{
    FsmState *state; ///< Pointer to the state array
    size_t states; ///< Number of states
    void **stateData; ///< Pointer to user state data array

    FsmEdge *edge; ///< Pointer to the edge transitions array
    size_t edges; ///< Number of edges
    void **edgeData; ///< Pointer to user edge data array

    FsmEvent event; ///< Current/last event
    fsm_e type; ///< Current/last action type

    FsmState *current; ///< Pointer to the current state
    void *data; ///< Per-fsm data
};

#define fsm_INIT { 0 }

static inline FsmEvent
fsmStep(Fsm *f, FsmEvent e)
{
    FsmState *cp = f->current; // Store current state
    FsmEvent ne = fsm_None; // Next event

    // User state data
    void *us = (f->stateData && cp) ? f->stateData[cp - f->state] : NULL;

    if (!cp && e == fsm_None)
        e = fsm_Reset; // Inject reset into uninitialized FSM

    f->event = e;

    switch (e)
    {
    case fsm_Reset:
        {
            // Exit current state
            if (cp && cp->action)
            {
                f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, f->state, us);

                if (ne != fsm_None)
                    return ne; // Leave action emitted event
            }

            FsmState *ps = cp;
            cp = f->current = f->state; // First state in array is entry state

            if (!cp)
                return fsm_End; // Null state machine

            if (cp->action)
            {
                us = f->stateData ? f->stateData[0] : NULL;
                f->type = fsm_Enter, ne = cp->action(cp, fsm_Enter, ps, us);
            }
        }
        break;

    case fsm_None: // No event, run in-state action
        if (cp->action)
            f->type = fsm_State, ne = cp->action(cp, fsm_State, NULL, us);
        break;

    default: // Process user transition event
        ne = fsm_Empty; // Default return in case no transition is found

        // Search transition in listing order
        for (size_t i = 0; i < f->edges; ++i)
        {
            FsmEdge *ep = &f->edge[i];

            // Check for state match (null edge state matches any state)
            if (ep->state && ep->state != cp)
                continue; // Not a match

            // Check for stop processing filter
            if (ep->event == fsm_End)
                break;

            ne = fsm_None; // Default return for a transition

            // Check for event match
            if (ep->event == e || ep->event == fsm_Any)
            {
                // User edge data
                void *ue = f->edgeData ? f->edgeData[i] : NULL;

                // Check transition guard
                if (!ep->guard || ep->guard(ep, cp, e, ue))
                {
                    // Resolve next state pointer (NULL, new state pointer or jump function)
                    FsmState *np = (!ep->next) ? NULL
                        : ((FsmState *)(ep->next) >= f->state && (FsmState *)(ep->next) < (f->state + f->states)) ? (FsmState *)(ep->next)
                        : ((FsmJump)(ep->next))(ep, cp, e, ue);

                    if (cp->action)
                    {
                        f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, np, us);

                        if (ne != fsm_None)
                            return ne; // Leave action emitted event
                    }

                    if (!np) // Final state reached if next state is NULL
                        ne = fsm_End;
                    else if (np->action)
                    {
                        us = f->stateData ? f->stateData[np - f->state] : NULL;
                        f->type = fsm_Enter, ne = np->action(np, fsm_Enter, cp, us);
                    }

                    cp = np; // New state value

                    // Transition executed, stop
                    break;
                }
            }
        }
    }

    f->current = cp; // Commit current state

    return ne; // Last event value
}

static inline FsmEvent
fsmReset(Fsm *f)
{
    return fsmStep(f, fsm_Reset);
}

#endif // FSM_H_

下面是一个非常简单的测试,可以了解如何定义和使用FSM实现。没有用户定义的事件,只有FSM数据(字符串),每个状态的相同状态操作和共享跳转功能:

#include <stdio.h>

#include "fsm.h"

// State action example
static FsmEvent
state_action(FsmState *s, fsm_e t, FsmState *ft, void *us)
{
    FsmEvent e = fsm_None; // State event
    const char *q = "?";

    switch (t)
    {
    case fsm_Enter:
        q = "enter";
        break;

    case fsm_Leave:
        q = "leave";
        break;

    default /* fsm_State */:
        q = "state";
    }

    printf("%s %s\n", (const char *)s->data, q);

    return e;
}

// States
FsmState fs[] =
{
    { state_action, "S0" },
    { state_action, "S1" },
    { state_action, "S2" },
};

// Transition jump example
static FsmState *
state_jump(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    if (s == &fs[0])
        return &fs[1];

    if (s == &fs[2])
        return NULL; // End state

    return NULL; // Trap
}

// Transition guard example
static bool
count_attempt(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    static int c = 0;
    ++c;
    bool b = c == 3;
    printf("guard is %s\n", b ? "true" : "false");
    return b;
}

// Transitions
FsmEdge fe[] =
{
    { &fs[0], fsm_Any, state_jump }, // Using jump function, no guard
    { &fs[1], fsm_Any, &fs[2], count_attempt }, // Using direct state and guard
    { &fs[2], fsm_Any, state_jump  }, // Using jump function, no guard
};

int
main(int argc, char **argv)
{
    Fsm f = { fs, 3, NULL, fe, 3, NULL, };

    fsmReset(&f);

    do 
    {
        fsmStep(&f, fsm_None);
    } while (fsmStep(&f, fsm_Any) != fsm_End);

    return 0;
}

Boost有状态图库。 http://www.boost.org/doc/库/ 1_36_0 /库/状态图/ DOC / index.html的

但是,我不能说它的使用。自己没用过(还)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top