Как использовать методы-члены в качестве обратных вызовов вместо статических методов для конечного автомата, такого как дизайн - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть некоторый код, который действует как простой конечный автомат. У меня есть следующие объекты:

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 использует StateMachine и Actions, которые включены в StateMachine, будут иметь экземпляр Device.

1 Ответ

0 голосов
/ 06 ноября 2018

Существует разница между типом void(*)(int) (который вы называете Actions::DoAction) и типом void(Device::*)(int), который &Device::TakeControl и т. Д.

Обратите внимание, что вам нужно и a Device и int для вызова последнего. К счастью, вы можете настроить 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::Action передать с Device *, вы можете использовать

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

Однако вы хотите связать определенный Action с обратным вызовом. Лямбда может захватывать значения для использования при вызове.

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) } };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...