代码之家  ›  专栏  ›  技术社区  ›  the_drow

在C语言中实现分层状态机

  •  5
  • the_drow  · 技术社区  · 14 年前

    我对如何实现状态机有点困惑。
    我已经知道这是分等级的,因为有些州也有同样的行动。
    我通过这些参数来确定我需要做什么:

    • 等级 (值为: 基地 , 衍生的 , 特定的 )
    • 操作码
    • 参数1-可选
    • 参数2-可选

    我的层次结构由类和 操作码 表示操作。
    衍生的 可以使用 操作码 属于 基地 特定的 可以使用 操作码 两者兼而有之 基地 衍生的 .
    简单的实现如下:

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

    这会很快变得无法弥补。
    有没有其他方法可以将状态映射到超类?

    编辑:
    进一步的计算使我认为,我可能会使用大部分,如果不是全部 操作码 但我不会用所有的 我可以。
    另一个澄清:
    一些 操作码 可以通过多个派生和基共享 .
    例如:

    • 我有一个 等级 打电话 任何 哪个是 基地 班级。它有 操作码 : Stisteon , 州立大学 , 状态集 .
    • 我还有另外一个 等级 打电话 MyGrand 哪个是 衍生的 班级。它有 操作码 : 状态翻转 , 状态触发器 .

    • 第三 等级 是一个 特定的 类称为 廷金集团 哪一个 有 操作码 : 状态触发器和触发器 .

    所以课堂上的一个信息 任何 从服务器发送,在所有客户端接收并处理。

    带类的消息 MyGrand 从服务器发送、在所有客户端中接收并仅在属于的客户端上处理 MyGrand 任何 操作码 有效期为 任何 类对于 MyGrand 班级。

    带类的消息 廷金集团 从服务器发送、在所有客户端中接收并仅在属于的客户端上处理 MyGrand 是一个 thinginmygroup*,任何**操作码 有效期为 任何 类和 MyGrand 类对于 廷金集团 班级。

    收到消息后,客户机将相应地确认/nack。

    我不喜欢使用开关盒或常量数组,因为当它们变大时,它们将变得不可维护。
    我需要一个灵活的设计,允许我:

    1. 指定 操作码 是可用的 对于每一个 等级 .
    2. 为每个指定超类 等级 并通过该规范允许我调用由当前值表示的函数指针 操作码 .
    2 回复  |  直到 10 年前
        1
  •  4
  •   nategoose    14 年前

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

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

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

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

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

    另一种更动态的处理方法是在每个“类”中都有一个父指针指向“类”,它可以处理它不能处理的任何事情。

    二维表方法快速且灵活(派生的处理程序可能与操作码0的基处理程序不同),但它增长很快。

        2
  •  1
  •   bstpierre Edgar Aviles    14 年前

    我编写了一个小工具,它可以生成类似于您基于小型语言的简单实现的代码。语言刚刚指定了状态操作码操作关系,所有操作都只是符合typedef的C函数。

    它没有处理HSM方面,但这将相对容易添加到语言中。

    我建议采用这种方法——创建一种小的语言,它为您提供一种描述状态机的清晰方法,然后根据该机器描述生成代码。这样,当您需要在一个月后插入一个新的状态时,整个事情就不会乱成一团地进行编辑了。

    如果您需要代码,请告诉我,我会确保它在某个地方仍然可用。