«Обобщенный» реализация конечного автомата

Мне часто приходится реализовывать объект, способный переключать свое поведение в ответ на команду пользователя. Например, это может быть случай, когда устройство представления класса подключено к ПК и управляется пользователем через графический интерфейс. В более общем смысле, устройство должно жить самостоятельно, со своим собственным расписанием операций.
введите описание изображения здесь
Поскольку я хотел бы «извлечь» это поведение из определенного класса устройств, чтобы улучшить повторное использование кода, здесь я предлагаю шаблонный класс конечных автоматов, использующий Qt. Я также привел пример использования в классе А. Что вы (более опытные программисты, чем я 🙂 думаете об этом? Это «правильный» способ создать такой класс? Есть ли проблемы с производительностью?

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

};

9

Решение

A. Что вы (более опытные программисты, чем я 🙂 думаете о
тот? Это «правильный» способ создать такой класс? Здесь
проблемы с производительностью ?

ХОРОШО! Я грубо посмотрел на ваш дизайн, и мне не очень-то хорошо от фреймворка общего назначения FSM. Это слишком узко, чтобы его можно было использовать в расширенном контексте. Некоторые точки критики:

  1. Вы зависите от Qt :(; по крайней мере, вы должны использовать компоненты C ++ STL для деталей вашей реализации.
  2. Ваши государства должны быть (специализированными) классами самостоятельно, которые реализуют
    Поведение напрямую. Сам класс FSM, должен быть независимым
    (особенно не реализовывать) от их поведения.
  3. Вы не поддерживаете более сложные диаграммы состояний, включая подсостояния
    (машины) / составные состояния, параллельные пути FSM (вилка,
    переходы), активные состояния (повторные асинхронные операции do), …

В общем, я бы рекомендовал следовать GoF State Pattern для реализации FSM. Для очень простых диаграмм состояний switch(event) case <event>: changeState(newState) может быть достаточно. Но отображение событий в виде записей методов FSM и их передача текущему экземпляру класса состояния делает всю конструкцию гораздо более гибкой. Подумайте о необязательных параметрах, которые идут вместе с конкретным событием, и вам нужно будет расширить дизайн конечного автомата для них.

В целом ваш подход к использованию CRTP для вашего конечного автомата это хорошая идея, но для того, что вы продемонстрировали, простой динамический полиморфизм (с использованием виртуальных функций-членов) также подойдет.

Что касается проблем с производительностью, не думайте, что вы столкнетесь с проблемами в вашей текущей среде, но это полностью зависит от того, где и в каком контексте вы хотите развернуть.

Я хочу порекомендовать вам взглянуть на мою платформу шаблонов State Machine Class. STTCL, который предоставляет различные аспекты C ++, основанные на шаблонах, для конечных автоматов, совместимых с UML 2.0, в соответствии с уже упомянутым шаблоном состояния GoF.

8

Другие решения

если это все еще актуально, я реализовал конечный автомат в C ++, который использует Object OP, его довольно просто использовать, и если вы посмотрите на main.cpp, то есть пример.

Код здесь и сейчас он скомпилирован как библиотека.

Конечный автомат

Дайте мне знать, если это то, что вы хотите!

Ура,

Andrea

1

По вопросам рекламы [email protected]