对于像设计这样的状态机,如何使用成员方法作为回调而不是静态方法

时间:2018-11-06 12:22:09

标签: c++ c++11

我有一些趋于像一个简单状态机的代码。这些是我拥有的对象:

class Actions
{
public:
    enum Action {
        action1,
        action2,
        action3
    };

    typedef void(*DoAction)(int);

    static void Start(int type) {

    }

    static void Stop(int type) {

    }

    static void NoAction(int type) {

    }
};

struct ActionCallback
{
    Actions::Action         action;
    Actions::DoAction       callback;
    ActionCallback(Actions::Action a, Actions::DoAction c) :action(a), callback(c) { }
    ActionCallback() : action(Actions::action1), callback(Actions::NoAction) { }
};

这里有一个Actions对象,它定义了事件和回调,还有一个ActionCallback对象,它包含一个动作和一个回调。

下面是我的StateMachine类-为简单起见,它使用int's作为状态:

struct StateMachine {
    using StateMachineMap = std::map<std::pair<int, int>, ActionCallback>;
    StateMachineMap m_stateMachineMap;

    StateMachine() {
        m_stateMachineMap[std::make_pair(1, 2)] = ActionCallback(Actions::Action::action1, Actions::Start);
        m_stateMachineMap[std::make_pair(1, 3)] = ActionCallback(Actions::Action::action2, Actions::Stop);
        m_stateMachineMap[std::make_pair(1, 4)] = ActionCallback(Actions::Action::action3, Actions::Start);
        m_stateMachineMap[std::make_pair(1, 5)] = ActionCallback(Actions::Action::action3, Actions::Stop);
        m_stateMachineMap[std::make_pair(1, 6)] = ActionCallback(Actions::Action::action2, Actions::Start);
        m_stateMachineMap[std::make_pair(1, 7)] = ActionCallback(Actions::Action::action1, Actions::NoAction);
    }

    void performAction(Actions::Action action) {

    }
};

这是我的主类,它将包含状态机并调用performAction

class Device {
    StateMachine stateMachine;
public:
    void TakeControl(int type) {

    }

    void ReleaseControl(int type) {

    }

    void NoAction(int type) {

    }
};

我真正想要的是:是否可以通过这种设计将Device方法作为回调传递,而不是传递Actions类的静态方法?

类似这样的东西:

m_stateMachineMap[std::make_pair(1, 2)] = ActionCallback(Actions::Action::action1, Device::Start); // will trigger Device::Start to be called  

现在,我觉得Device使用StateMachineActions的{​​{1}}和StateMachine使用Device的实例是错误的。

1 个答案:

答案 0 :(得分:1)

类型void(*)(int)(别名为Actions::DoAction)和类型void(Device::*)(int)&Device::TakeControl等)之间是有区别的。

请注意,您都需要 Deviceint来调用后者。幸运的是,您可以改编ActionCallback来使用它。

struct ActionCallback
{
    using DeviceCall = void(Device::*)(Actions::Action);

    Actions::Action  action = Actions::action1;
    DeviceCall       callback = &Device::NoAction;
    void operator()(Device * device) { (device.*callback)(action); }
};

我们已经定义了operator(),因此可以像函数一样调用ActionCallback的实例

struct StateMachine {
    using StateMachineMap = std::map<std::pair<int, int>, ActionCallback>;
    StateMachineMap m_stateMachineMap;

    StateMachine() {
        m_stateMachineMap[std::make_pair(1, 2)] = ActionCallback{Actions::Action::action1, &Device::Start};
        m_stateMachineMap[std::make_pair(1, 3)] = ActionCallback{Actions::Action::action2, &Device::Stop};
        m_stateMachineMap[std::make_pair(1, 4)] = ActionCallback{Actions::Action::action3, &Device::Start};
        m_stateMachineMap[std::make_pair(1, 5)] = ActionCallback{Actions::Action::action3, &Device::Stop};
        m_stateMachineMap[std::make_pair(1, 6)] = ActionCallback{Actions::Action::action2, &Device::Start};
        m_stateMachineMap[std::make_pair(1, 7)] = ActionCallback{Actions::Action::action1, &Device::NoAction};
    }

    void performAction(std::pair<int, int> what, Device * who) {
        m_stateMachineMap[what](who);
    }
};

还存在一种类型,该类型可以概括具有给定签名std::function的任何可调用对象。

如果将Actions::ActionDevice *一起传递,则可以使用

using ActionCallback = std::function<void(Device *, Actions::Action)>;
StateMachineMap m_stateMachineMap { { std::make_pair(1, 2), &Device::Start } };

但是,您要将特定的Action与回调相关联。 Lambda可以捕获调用时要使用的值。

using DeviceCall = void(Device::*)(Actions::Action);
using ActionCallback = std::function<void(Device *)>;
ActionCallback make_action_callback(Actions::Action action, DeviceCall callback)
{
    return [=](Device * device){ (device.*callback)(action); };
}

StateMachineMap m_stateMachineMap { { std::make_pair(1, 2), make_action_callback(Actions::action1, &Device::Start) } };