状态机更改状态

时间:2020-10-27 21:30:05

标签: c++ state-machine

我一直在遇到相同的问题,即使在阅读教程时也无法解决。 我已经“设置”了状态机,但无法在状态之间进行转换。

这是我的StateMachine:

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,这使我在其中擦洗头痛苦。

2 个答案:

答案 0 :(得分:0)

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

访问StateMachine

好吧,这是State Pattern的一个众所周知的问题,没有提到如何使用封闭的 State Machine 来保持状态类的状态。

IMO,这是将StateMachine类实现为Singleton的有效用例之一。
这样,就可以从任何State类实现中访问其实例。

正如我在这里所说的设计模式一样, State 类可以借助Flyweight Pattern进行设计,因为它们本身通常是无状态的。

我曾经将所有这些驱动到一个c ++模板框架中,该框架抽象了 State State 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相同。


参考:

答案 1 :(得分:0)

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

我个人是使用JK触发器设计电子设备中的状态机的人,因此我会使用类似但语义上不同的方法。状态机的复杂性包括根据状态和输入执行的 action 。通常,在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
    }
}