【问题标题】:State Machine Change State状态机改变状态
【发布时间】:2020-10-27 21:30:05
【问题描述】:

我不断遇到同样的问题,即使查看教程也无法解决。 我已经“设置”了我的状态机,但我无法在状态之间转换。

这是我的状态机:

class StateMachine
{
    State* m_State;

public:
    StateMachine();
    ~StateMachine();
    void changeState(State* state);
};

这是一个示例状态:

class A : State
{
public:
    A();
    ~A();
    void handleInput(int a);
}

如果我将 a = 1 传递给 A::handleInput(),我想转换到状态 B。但是当我实现它时,我无法从 A::handleInput() 访问 StateMachine,这让我不得不摸索痛苦。

【问题讨论】:

  • 这是State Pattern 的常见问题。幸运的是,对于这种情况,前向引用就足够了,当然可以访问StateMachine 类,
  • 不相关的旁注:如果你真的需要这些析构函数,请确保you don't also need its friends
  • 恕我直言,一个状态机 包含一个或多个状态。因此应该有状态的容器,而不是指针。一个状态可能需要一个 transition 方法,该方法根据给定的输入返回 下一个 状态。
  • @Okami 您可能对我几年前所做的这项工作感兴趣:State machine template class framework for C++ 这有点复杂,但有据可查,希望易于使用。跨度>
  • 令人惊讶的是,我在该代码中看到的希腊字符很少......

标签: c++ state-machine


【解决方案1】:

但是当我实现它时,我无法从 A::handleInput() 访问 StateMachine

嗯,这是State Pattern 的一个众所周知的问题,没有提及如何使用封闭的状态机 来保持状态类的正常运行。

IMO,这是考虑将StateMachine 类实现为Singleton 的有效用例之一。
这样,任何Stateclass 实现都可以访问它的实例。

正如我在这里讨论的设计模式,State 类可以在 Flyweight Pattern 的帮助下设计,因为它们本身通常是无状态的。

我曾经将所有这些驱动到一个 c++ 模板框架中,该框架抽象了 StateState Machine 的接口(参见下面的链接)。

下面是一个简短的代码示例:

StateMachine.h

struct State {
    virtual void handleInput(int x) = 0;
    virtual ~State() {} = 0;
};

class StateMachine {
    State* m_State;

    StateMachine();
public:
   
    static StateMachine& instance() {
        static StateMachine theInstance;
        return theInstance;
    }
    void changeState(State* state) {
        m_State = state;
    }
    void triggerInput(int x) {
        m_State->handleInput(x);
    }
};

StateA.h

#include "StateMachine.h"
class StateB;
extern StateB* stateB;

class StateA : public State {
public:
    virtual ~StateA() {}
    virtual void handleInput(int x) {
        if(x == 1) {
             // Change to StateB
             StateMachine::instance.changeState(stateB);
        }
        else {
            // Do something with x
        }
    }
};

这里省略了StateB的定义,应该和StateA一样。


参考资料:

【讨论】:

    【解决方案2】:

    我查看了 Sourcemaking 示例,对我来说,实现示例真的很糟糕;必须在每次状态更改时创建新实例: https://sourcemaking.com/design_patterns/state/cpp/1

    作为一个使用 JK 触发器在电子设备中设计状态机的人,我会使用类似但语义不同的方法。状态机的复杂性涉及根据状态和输入执行的动作;通常在 C 语言中,您会使用大量 switch 语句和可能的数组来描述如何处理 current statenew input aka event

    所以对我来说,面向对象的方法是对event handler 建模。这将有一个描述输入格式的接口。然后,对于每个不同的状态,您都有该接口的不同实现。有了它,状态机可以简单地为事件处理程序实现一组状态——数组、向量或映射。尽管处理程序仍可能包含 case 语句,但整体的意大利面性已大大降低。您可以在必要时使用新的状态处理程序轻松扩展设计:

    所以你可以有这样的东西:

    
    #include <map>
    
    typedef enum
    {
        //TODO : state list, e.g.
        eOff,
        eOn
    }
    teCurrentState;
    
    typedef struct
    {
        //TODO : Add inputs here, e.g.
        bool switch1;
    }
    tsInputDesc;
    
    typedef struct
    {
        //TODO : Add outputs here, e.g.
        bool relay1;
    }
    tsOutputDesc;
    
    // ------------------------------------------------
    
    class IEventHandler
    {
    public:
        virtual ~IEventHandler() {}
        // returns new state
        virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) = 0;
    };
    
    // ------------------------------------------------
    
    class OnStateHandler : public IEventHandler
    {
    public:
        virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
        {
            //TODO : IMPLEMENT
            teCurrentState newState = TODO....
            return (newState);
        }
    };
    
    // ------------------------------------------------
    
    class OffStateHandler : public IEventHandler
    {
    public:
        virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
        {
            //TODO : IMPLEMENT
            teCurrentState newState = TODO....
            return (newState);
        }
    };
    
    // ------------------------------------------------
    
    class StateMachine
    {
    protected:
        teCurrentState mCurrentState;
        std::map<teCurrentState, IEventHandler*> mStateHandlers;
    
        void makeHandlers()
        {
            mStateHandlers[eOff] = new OffStateHandler();
            mStateHandlers[eOn] = new OnStateHandler();
        }
    
    public:
        StateMachine()
        {
            makeHandlers();
            mCurrentState = eOff;
        }
    
        void handleInput(tsInputDesc const& input, tsOutputDesc output)
        {
            teCurrentState newState = mStateHandlers[mCurrentState]->handleInput(input, output);
            mCurrentState = newState;
        }
    };
    
    // ------------------------------------------------
    
    void runFsm()
    {
        StateMachine fsm;
        tsInputDesc input;
        tsOutputDesc output;
        bool alive = true;
    
        while (alive)
        {
            // TODO : set input according to....inputs (e.g. read I/O port etc)
    
            fsm.handleInput(input, output);
    
            // TODO : use output
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-01
      • 2019-07-25
      • 2020-10-13
      相关资源
      最近更新 更多