关于 c :A \\”generalized\\” 有限状态机实现

A "generalized" finite state machine implementation

我经常需要实现一个能够根据用户命令切换其行为的对象。例如,这可能是连接到 PC 并由用户通过 GUI 控制的类表示设备的情况。更一般地说,设备必须独立运行,并具有自己的操作调度。
enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
template < class Base,
           typename T,
           class ThreadPolicy>
class FSM
{
public:
    typedef bool (Base::*my_func)();
    struct SState {
        SState(){}
        SState(const T& id_arg,
               const T& next_arg,
               const T& error_arg,
               const QList< T >& branches_arg,
               const my_func& op_arg) :
            id(id_arg),
            next(next_arg),
            error(error_arg),
            branches(branches_arg),
            op(op_arg)
        {}
        T id;       // state ID
        T next;    // next state
        T error;    // in case of error
        QList< T > branches; // allowed state switching from current
        my_func op; // operation associated with current state
    };
    typedef QMap<T ,SState> SMap;
    bool switchState(const T& ns){
        return _checkAllowed(ns);
    }
    bool addState(const T& id, const SState& s){
        return _register(id, s);
    }
protected:

    void _loop(Base* ptr){
        if ((ptr->*m_states[m_state].op)()) {
            ThreadPolicy::Lock();
            if(m_externalSwitch){
                m_externalSwitch = false;
                ThreadPolicy::Unlock();
                return;
            }
            m_state = m_states[m_state].next;
            ThreadPolicy::Unlock();
        } else {
            ThreadPolicy::Lock();
            if(m_externalSwitch){
                m_externalSwitch = false;
                ThreadPolicy::Unlock();
                return;
            }
            m_state = m_states[m_state].error;
            ThreadPolicy::Unlock();
        }
    }
    bool _checkAllowed(const T& cmd){
        if (!m_states[m_state].branches.contains(cmd)) { return false;}
        ThreadPolicy::Lock();
        m_state = cmd;
        m_externalSwitch = true;
        ThreadPolicy::Unlock();
        return true;
    }

    bool _register(const SState& s){
        if(m_states.find(s.id) != m_states.end()) { return false; } // state with same ID already exist
        m_states[s.id] = s; // add the new state to the map
        return true;
    }
    SMap m_states; // map states to Baseclass methods
    T m_state;  // holds my current state
    bool m_externalSwitch; // check if user request a state switch
};

class A :
        public QObject,
        public FSM< A, QString, MultiThreaded >
{
    Q_OBJECT
    A(){
//        SState startState; myState.branches <<"start" <<"stop";
        _register(SState("start",
                        "start",
                        "stop",QStringList(("start","stop")),
                         &A::_doStart));
        _register(SState("stop",
                        "stop",
                        "stop",QStringList(("stop","start")),
                         &A::_doStop));
    }

private slots:
    void run(){
        for(;;){
            _loop(this);
            QCoreApplication::processEvents();
        }
    }
private:
    bool _doStart(){ return true;}
    bool _doStop(){ return true;}

};


A. What do you ( more experienced programmers than me :) think about
that? Is it the"correct" way to design such a class? Are there
performance issues ?

好的!我对您的设计进行了粗略的了解,对于通用 FSM 框架,我感觉并不好。这太窄了,无法在更广泛的环境中使用。几点批评:

  • 您依赖于 Qt :( ;至少您应该使用 C STL 组件作为实现细节。
  • 您的状态应该是(专门的)类,它们实现了
    直接行为。 FSM 类本身,应该是独立的
    (尤其是不执行)从他们的行为。
  • 您不支持更复杂的状态图,包括子状态
    (机器)/复合状态,并发 FSM 路径(fork,
    路口),活动状态(重复的异步执行操作),...
  • 一般来说,我建议遵循 GoF 状态模式来实现 FSM。对于非常简单的状态图,switch(event) case <event>: changeState(newState) 可能就足够了。但是将事件呈现为 FSM 的方法条目,并将它们委托给当前状态类实例,使得整个构造更加灵活。考虑与特定事件一起出现的可选参数,您需要针对这些扩展您的状态机设计。

    一般而言,您为状态机使用 CRTP 的方法是一个好主意,但对于您演示的内容,简单的动态多态性(使用虚拟成员函数)也可以很好地工作。

    关于性能问题,不要认为您会在当前环境中遇到问题,但这完全取决于您要部署的位置和环境。

    我想建议你看看我的状态机类模板框架 STTCL,它提供了各种基于 C 模板的 UML 2.0 兼容状态机,遵循已经提到的 GoF 状态模式。


    如果它仍然相关,我已经在 C 中实现了一个使用 Object OP 的有限状态机,它使用起来相当简单,如果你查看 main.cpp 有一个例子。

    代码在这里,它现在被编译为一个库。

    有限状态机

    如果是你想要的,请告诉我!

    干杯,

    安德烈亚