A "generalized" finite state machine implementation
我经常需要实现一个能够根据用户命令切换其行为的对象。例如,这可能是连接到 PC 并由用户通过 GUI 控制的类表示设备的情况。更一般地说,设备必须独立运行,并具有自己的操作调度。
由于我想从特定设备类中"提取"这种行为以增强代码重用,因此我提出了一个使用 Qt 的模板化有限状态机类。我还报告了 A 类中的一个示例用法。您(比我更有经验的程序员 :) 对此有何看法?这是设计这样一个类的"正确"方式吗?是否存在性能问题?
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 框架,我感觉并不好。这太窄了,无法在更广泛的环境中使用。几点批评:
直接行为。 FSM 类本身,应该是独立的
(尤其是不执行)从他们的行为。
(机器)/复合状态,并发 FSM 路径(fork,
路口),活动状态(重复的异步执行操作),...
一般来说,我建议遵循 GoF 状态模式来实现 FSM。对于非常简单的状态图,
一般而言,您为状态机使用 CRTP 的方法是一个好主意,但对于您演示的内容,简单的动态多态性(使用虚拟成员函数)也可以很好地工作。
关于性能问题,不要认为您会在当前环境中遇到问题,但这完全取决于您要部署的位置和环境。
我想建议你看看我的状态机类模板框架 STTCL,它提供了各种基于 C 模板的 UML 2.0 兼容状态机,遵循已经提到的 GoF 状态模式。
如果它仍然相关,我已经在 C 中实现了一个使用 Object OP 的有限状态机,它使用起来相当简单,如果你查看 main.cpp 有一个例子。
代码在这里,它现在被编译为一个库。
有限状态机
如果是你想要的,请告诉我!
干杯,
安德烈亚