用C语言实现分层状态机

the*_*row 5 c state-machine hsm

我对如何实现我的状态机感到有点困惑.
我已经知道它是分层的,因为一些州共享相同的行动.
我通过这些参数确定我需要做什么:

  • (值为:基数,派生,特定)
  • 操作码
  • 参数1 - 可选
  • 参数2 - 可选

我的层次结构由Class决定,OpCode表示操作.
衍生可以使用操作码基础具体可以使用操作码两者的基础衍生.
天真的实现如下:

void (*const state_table [MAX_CLASSES][MAX_OPCODES]) (state *) {
  {base_state1, base_state2, NULL, NULL},
  {base_state1, base_state2, derived_state1, NULL},
  {base_state1,base_state2, derived_state1, specific_state3},
};

void dispatch(state *s)
{
  if (state_table[s->Class][s->OpCode] != NULL)
    state_table[s->Class][s->OpCode](s);
}
Run Code Online (Sandbox Code Playgroud)

这将变得难以维持.
还有另一种方法将状态映射到超类吗?

编辑:
进一步calcualtion使我认为,我可能会用最如果不是所有的操作码,但我不会用所有的提供给我.
另一个澄清:
一些OpCodes可能通过多个派生和基共享.
例如:

  • 我有一个名为Any ,它是一个Base类.它有 OpCodes:STATE_ON,STATE_OFF,STATE_SET.
  • 我有另一个名为 MyGroup的,它是一个Derived类.它有OpCodes: STATE_FLIP,STATE_FLOP.

  • 第三个是一个 名为ThingInMyGroup特定类,它具有OpCode: STATE_FLIP_FLOP_AND_FLOOP.

因此,从服务器发送包含Any类的消息,在所有客户端中接收并处理.

与A级消息MyGroup的是从服务器发送,收到的所有客户端和处理只对属于客户MYGROUP,任何操作码的有效期为在任何类是有效的MyGroup的类.

从服务器发送带有ThingInMyGroup类的消息,在所有客户端中收到并仅在属于MyGroup的客户端上处理并且是ThingInMyGroup*,任何Any类和MyGroup类有效的**OpCodeThingInMyGroup类都有效.

收到消息后,客户端将相应地ACK/NACK.

我不喜欢使用switch case或const数组,因为当它们变大时它们将变得不可维护.
我需要一个灵活的设计,让我:

  1. 指定每个可用的OpCode.
  2. 为每个Class指定一个超类,并通过该规范允许我调用当前OpCode表示的函数指针.

nat*_*ose 4

有几种方法可以解决这个问题。这是一个:

编辑——添加了通用层次结构

typedef unsigned op_code_type;
typedef void (*dispatch_type)(op_code_type);
typedef struct hierarchy_stack hierarchy_stack;
struct hierarchy_stack {
       dispatch_type func;
       hierarchy_stack *tail;
};

void dispatch(state *s, hierarchy_stack *stk) {
    if (!stk) {
          printf("this shouldn't have happened");
    } else {
          stk->func(s, stk->tail);
    }
}

void Base(state *s, hierarchy_stack *stk ) {
    switch (s->OpCode) {
          case bstate1:
               base_state1(s);
               break;
          case bstate2:
               base_state(2);
               break;
          default:
               dispatch(s, stk);
    }
}
void Derived(state *s, hierarchy_stack *stk ) {
    switch(s->opcode) {
           case dstate1:
                deriveds_state1(s);
                break;
           default:
                dispatch(s, stk);
    }
}
... 
Run Code Online (Sandbox Code Playgroud)

注意:所有函数调用都是尾调用。

这会很好地本地化您的“类”,因此,如果您决定 Derived 需要 100 个以上的方法/操作码,那么您只需编辑用于定义操作码的方法和枚举(或其他内容)。

另一种更动态的处理方法是在每个“类”中都有一个父指针,该指针指向将处理它无法处理的任何内容的“类”。

2D 表方法快速且灵活(对于操作码 0,Derived 可能具有与 Base 不同的处理程序),但增长速度很快。