Государственные машины в C

Каков наилучший способ записи конечного автомата в C?
Обычно я пишу большой оператор switch-case в for (;;), с обратными вызовами для повторного входа в конечный автомат, когда внешняя операция завершена.
Знаете ли вы более эффективный способ?

Ответы

Ответ 1

Мне нравится подход Quantum Leaps.

Текущее состояние является указателем на функцию, которая принимает объект события в качестве аргумента. Когда происходит событие, просто вызовите функцию состояния с этим событием; Затем функция может выполнить свою работу и перейти в другое состояние, просто установив состояние в другую функцию.

например:.

// State type and variable, notice that it a function pointer.
typedef void (*State)(int);
State state;

// A couple of state functions.
void state_xyz(int event) { /*...*/ }
void state_init(int event) {
    if (event == E_GO_TO_xyz) {
        // State transition done simply by changing the state to another function.
        state = state_xyz;
    }
}

// main contains the event loop here:
int main() {
    int e;
    // Initial state.
    state = state_init;
    // Receive event, dispatch it, repeat... No 'switch'!
    while ((e = wait_for_event()) != E_END) {
        state(e);
    }
    return 0;
}

Структуры QL предоставляют помощники для дополнительных вещей, таких как операции ввода/выхода/init, иерархические государственные машины и т.д. Я настоятельно рекомендую книгу для более глубокое объяснение и хорошая реализация этого.

Ответ 2

Лучший способ в значительной степени субъективен, но распространенным способом является использование "основанного на таблице" подхода, когда вы указываете коды состояний (перечисления или какой-либо другой интегральный тип) на указатели функций. Функция возвращает ваше следующее состояние и другие связанные данные, и вы выполняете цикл до тех пор, пока не будет достигнуто состояние терминала. На самом деле это может быть то, что вы описываете как ваш подход выше.

Ответ 3

Это в значительной степени стандартный подход. Если вы заинтересованы в изучении хорошо продуманной библиотеки и сравнении ее особенностей, посмотрите Ragel:

Ragel компилирует исполняемые конечные машины с регулярными языками. Ragel нацелен на C, С++, Objective-C, D, Java и Ruby. Ранговые машины Ragel могут не только распознавать последовательности байтов, как машины с регулярными выражениями, но также могут выполнять код в произвольных точках при распознавании обычного языка. Встраивание кода выполняется с использованием встроенных операторов, которые не нарушают синтаксис регулярного языка.

Ответ 5

Альтернативный подход - это 2D-массив, который описывает для каждой комбинации состояний/событий действия для выполнения и следующее состояние. Это может стать более сложным для управления, когда вам нужно перейти в разные штаты в зависимости от "обстоятельств", но это может быть сделано для того, чтобы хорошо работать. У вас есть функция распознавания событий, которая возвращает следующее событие; у вас есть таблица, в которой каждая запись в таблице идентифицирует функцию для вызова при получении события и следующего состояния для перехода к - если вызываемая функция не отменяет это состояние.

На самом деле генерация такого кода является fiddlier - это зависит от того, как описывается FSM в первую очередь. Часто важно дублировать повторяющиеся действия. Часто вы можете полагаться на методы "разреженной матрицы", которые явно не фиксируют обработку ошибок: если запись логически существует в разреженной матрице, вы действуете на эту информацию о событии/состоянии, но если запись не существует, вы возвращаетесь к соответствующим отчет об ошибках и код повторной синхронизации.

2D-массив указателей на структуры может быть передан в общую функцию FSM; тот факт, что вы пишете тройной указатель, достаточно, чтобы вы были осторожны в том, что происходит. (Я написал один из них еще в марте 1986 года - у меня больше нет источника для этого на диске, хотя у меня все еще есть распечатка документа, который его описал.)

Ответ 6

Я использовал этот шаблон. Существует ли типичный шаблон реализации государственного аппарата? (проверьте лучший ответ).

Но я также добавляю некоторые функции
1. Информация о предыдущем состоянии.
2. Передача параметров 3. Добавление внешних событий, таких как глобальный тайм-аут и "переопределение SM"

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

Ответ 7

Посмотрите здесь: http://code.google.com/p/fwprofile/

Это версия с открытым исходным кодом (GNU GPLv3), реализованная на государственной машине в C. Концепция и реализация хорошо подходят для использования в критически важных приложений. Существуют развертывания в промышленных приложения.

Ответ 8

Я использую указатели функций и таблицу поиска 2d, где я использую состояние для одного параметра, а событие - как другое.

Я использую excel (или любой инструмент для работы с электронными таблицами) для сопоставления функции с каждой комбинацией состояний/событий.

Когда происходит событие, я помещаю его в очередь, поэтому у меня есть что-то похожее на это

int main(void)
{
    StateList currentState = start_up;
    EventList currentEvent;

    uint8_t stateArray[STATE_COUNT][EVENT_COUNT];

    InitializeStateArray(stateArray);
    InitializeEventQue();

    while(1)
    {
        currentEvent = GetPriorityEvent();
        currentState = (StateList)(*(stateArray[currentState][currentEvent]))();
    }
    return 1;  //should never get here
}

Этот метод существенно заставляет разработчика учитывать все возможные события в каждом состоянии, и по моему опыту упрощает отладку.

Ответ 9

Вы можете использовать минималистский фреймворк uml-state-machine, реализованный в c. Он поддерживает как конечный, так и иерархический конечный автомат. Каркас очень минималистичный. Он имеет только 3 API, 2 структуры и 1 перечисление.

Конечный автомат представлен структурой state_machine_t. Это абстрактная структура, которая может быть унаследована для создания конечного автомата.

//! Abstract state machine structure
struct state_machine_t
{
   uint32_t Event;          //!< Pending Event for state machine
   const state_t* State;    //!< State of state machine.
};

Состояние представлено указателем на структуру state_t в структуре.

Если структура настроена для конечного автомата, то state_t содержит,

typedef struct finite_state_t state_t;

// finite state structure
typedef struct finite_state_t{
  state_handler Handler;        //!< State handler function (function pointer)
  state_handler Entry;          //!< Entry action for state (function pointer)
  state_handler Exit;           //!< Exit action for state (function pointer)
}finite_state_t;

Если инфраструктура настроена для поддержки иерархического конечного автомата. Он содержит три дополнительных члена для представления иерархических отношений между состояниями.

typedef struct hierarchical_state_t state_t;

//! Hierarchical state structure
typedef struct hierarchical_state_t
{
  state_handler Handler;      //!< State handler function
  state_handler Entry;        //!< Entry action for state
  state_handler Exit;         //!< Exit action for state.

  const state_t* const Parent;    //!< Parent state of the current state.
  const state_t* const Node;      //!< Child states of the current state.
  uint32_t Level;                 //!< Hierarchy level from the top state.
}hierarchical_state_t;

Инфраструктура предоставляет API dispatch_event для отправки события на конечный автомат и два API для обхода состояния.

state_machine_result_t dispatch_event(state_machine_t* const pState_Machine[], uint32_t quantity);
state_machine_result_t switch_state(state_machine_t* const pState_Machine, const state_t* pTarget_State);

state_machine_result_t traverse_state(state_machine_t* const pState_Machine, const state_t* pTarget_State);

Подробнее об этом можно узнать из проекта GitHub.