【问题标题】:A "generalized" finite state machine implementation“广义”有限状态机实现
【发布时间】:2013-12-21 14:50:36
【问题描述】:

我经常需要实现一个能够根据用户命令切换其行为的对象。例如,这可能是连接到 PC 并由用户通过 GUI 控制的类表示设备的情况。更一般地说,设备必须独立运行,并具有自己的操作调度。 由于我想从特定设备类中“提取”这种行为以增强代码重用,因此我提出了一个使用 Qt 的模板化有限状态机类。我还报告了 A 类中的一个示例用法。您(比我更有经验的程序员 :) 对此有何看法?这是设计这样一个类的“正确”方法吗?是否存在性能问题?

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

};

【问题讨论】:

  • 顺便说一句,如果您只想知道您的代码是否良好,那么您提出的问题更适合 SO Code-Review 网站:codereview.stackexchange.com

标签: c++ templates state-machine


【解决方案1】:

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

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

Finite State Machine

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

干杯,

安德烈亚

【讨论】:

    【解决方案2】:

    A.你(比我更有经验的程序员 :) 怎么想 那?这是设计这样一个类的“正确”方式吗?在那儿 性能问题?

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

    1. 您依赖于 Qt :( ;至少您应该使用 C++ STL 组件来获取实现细节。
    2. 您的状态应该是(专门的)类,它们实现了 直接行为。 FSM 类本身,应该是独立的 (尤其是不实施)他们的行为。
    3. 您不支持更复杂的状态图,包括子状态 (机器)/复合状态,并发 FSM 路径(fork, 结),活动状态(重复的异步执行操作),...

    一般来说,我建议遵循GoF State Pattern 进行 FSM 实施。对于非常简单的状态图,switch(event) case &lt;event&gt;: changeState(newState) 可能就足够了。但是将事件呈现为 FSM 的方法条目,并将这些委托给当前状态类实例,使得整个构造更加灵活。考虑一下伴随特定事件的可选参数,您需要针对这些扩展您的状态机设计。

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

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

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

    【讨论】:

      猜你喜欢
      • 2012-06-26
      • 1970-01-01
      • 2011-03-31
      • 2020-11-18
      • 2018-11-13
      • 2014-01-09
      • 1970-01-01
      • 2017-01-04
      • 1970-01-01
      相关资源
      最近更新 更多