127

我们需要在C中实现一个简单的状态机。
标准的 switch 语句是最好的方法吗?
我们有一个当前状态(state)和一个转换触发器。


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++,我认为 Boost Statechart库可能是要走的路。但是,它对C没有帮助。让我们专注于 C 用例。

4

20 回答 20

152

我更喜欢对大多数状态机使用表驱动方法:

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

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

于 2008-09-25T13:35:31.257 回答
27

您可能已经看到我对另一个 C 问题的回答,其中我提到了 FSM!这是我的做法:

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:

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

在其他实现 FSM 的技术中,转换的结构隐藏在控制结构中(while、if、switch ...)并由变量值(通常是state变量)控制,将漂亮的图表与复杂的代码。

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

于 2008-09-25T13:35:49.133 回答
15

我也使用了表格方法。但是,有开销。为什么要存储第二个指针列表?C 中没有 () 的函数是 const 指针。所以你可以这样做:

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

当然,根据您的恐惧因素(即安全与速度),您可能需要检查有效指针。对于大于三个左右状态的状态机,上面的方法应该比等效的开关或表格方法更少的指令。你甚至可以宏化为:

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

另外,我从 OP 的示例中感觉到,在考虑/设计状态机时应该进行简化。我不认为过渡状态应该用于逻辑。每个状态函数都应该能够在不明确了解过去状态的情况下执行其给定的角色。基本上,您设计的是如何从您所处的状态过渡到另一个状态。

最后,不要基于“功能”边界开始设计状态机,为此使用子功能。而是根据您何时必须等待某些事情发生才能继续进行划分状态。这将有助于最大限度地减少在获得结果之前必须运行状态机的次数。这在编写 I/O 函数或中断处理程序时可能很重要。

此外,经典 switch 语句的一些优点和缺点:

优点:

  • 它是用语言编写的,因此有记录且清晰
  • 状态在它们被调用的地方定义
  • 可以在一个函数调用中执行多个状态
  • 所有状态通用的代码都可以在switch语句之前和之后执行

缺点:

  • 可以在一个函数调用中执行多个状态
  • 所有状态通用的代码都可以在switch语句之前和之后执行
  • 开关实现可能很慢

请注意这两个属性既是赞成又是反对。我认为这种切换为状态之间提供了过多共享的机会,并且状态之间的相互依赖可能变得难以管理。然而,对于少数状态,它可能是最可读和可维护的。

于 2012-02-17T03:37:59.173 回答
13

Martin Fowler 的 UML Distilled中,他在第 10 章状态机图(强调我的)中指出(没有双关语):

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

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

在此处输入图像描述

嵌套开关

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

在此处输入图像描述

状态表

从福勒那里得到灵感,下面是我的示例表:

源状态目标状态事件保护动作
-------------------------------------------------- ----------------------------------
ScreenOff ScreenOff pressButton powerLow displayLowPowerMessage  
ScreenOff ScreenOn pressButton !powerLow
ScreenOn ScreenOff 按下按钮
ScreenOff Screen充电插头电源
ScreenOn Screen充电插头电源
ScreenCharging ScreenOff 拔掉电源

比较

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

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

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

编辑(不是真正的 C 语言)

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

于 2017-07-06T17:14:27.250 回答
11

随着状态机变大,还有更易于维护的逻辑网格

于 2008-09-25T13:24:00.643 回答
10

对于简单的状态机,只需为您的状态使用 switch 语句和枚举类型。根据您的输入在 switch 语句中进行转换。在实际程序中,您显然会更改“if(input)”以检查您的转换点。希望这可以帮助。

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;
    }
    ...
}
于 2008-09-25T19:36:45.427 回答
4

对于简单的情况,您可以使用您的 switch 样式方法。我发现过去效果很好的是也可以处理转换:

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 库一无所知,但是这种方法非常简单,不需要任何外部依赖项,并且易于实现。

于 2008-09-25T13:25:30.903 回答
4

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;
}
于 2008-09-25T20:58:42.343 回答
4

我在 edx.org 课程 Embedded Systems - Shape the World UTAustinX - UT.6.02x,第 10 章,由 Jonathan Valvano 和 Ramesh Yerraballi 找到了一个非常巧妙的 Moore FSM C 实现......

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];  
  }
}
于 2015-04-29T02:10:10.400 回答
2

这篇文章是状态模式的好文章(虽然它是 C++,而不是专门的 C)。

如果你能上手《Head First Design Patterns》这本书,说明和例子都非常清楚。

于 2008-09-25T13:26:37.250 回答
2

您可能想查看libero FSM 生成器软件。从状态描述语言和/或(Windows)状态图编辑器中,您可以为 C、C++、java 和许多其他语言生成代码......加上漂亮的文档和图表。来自iMatix的源代码和二进制文件

于 2008-09-26T14:07:19.133 回答
2

我最喜欢的模式之一是状态设计模式。对相同的给定输入集做出不同的响应或行为。
将 switch/case 语句用于状态机的问题之一是,当您创建更多状态时,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 都可以合并到一个文件中。

于 2013-05-26T05:47:29.567 回答
1

根据我的经验,使用“switch”语句是处理多种可能状态的标准方法。尽管我很惊讶您将转换值传递给每个状态处理。我认为状态机的全部意义在于每个状态都执行一个动作。然后下一个动作/输入确定要转换到哪个新状态。因此,我希望每个状态处理函数立即执行为进入状态而固定的任何内容,然后再决定是否需要转换到另一个状态。

于 2008-09-25T13:13:05.397 回答
1

有一本书名为Practical Statecharts in C/C++但是,对于我们需要的东西来说,它太重了。

于 2008-09-25T13:34:21.210 回答
1

对于支持的编译器__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__

于 2015-03-20T08:29:21.863 回答
1

您可以在 c 中使用极简的 UML 状态机框架。https://github.com/kiishor/UML-State-Machine-in-C

它支持有限和分层状态机。它只有 3 个 API、2 个结构和 1 个枚举。

状态机由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;

该框架提供了一个 APIdispatch_event来将事件分派到状态机和两个用于状态遍历的​​ API。

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.md
https://github.com/kiishor/UML-State-Machine-in -C/blob/master/demo/simple_state_machine_enhanced/readme.md

于 2019-08-15T17:18:50.220 回答
0

在 C++ 中,考虑状态模式

于 2008-09-25T13:23:27.337 回答
0

您的问题类似于“是否有典型的数据库实现模式”?答案取决于你想达到什么目标?如果您想实现更大的确定性状态机,您可以使用模型和状态机生成器。示例可在 www.StateSoft.org - SM Gallery 查看。雅努什·多布罗沃尔斯基

于 2010-03-10T19:36:23.437 回答
0

我也更喜欢表驱动的方法。我过去使用过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");
}

于 2021-08-13T17:30:03.657 回答
-1

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

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

于 2008-09-25T13:11:48.020 回答