Нам необходимо реализовать простую государственную машину в C .
Стандартный оператор switch - лучший способ?
У нас есть текущее состояние (состояние) и триггер перехода.
switch(state)
{
case STATE_1:
state = DoState1(transition);
break;
case STATE_2:
state = DoState2(transition);
break;
}
...
DoState2(int transition)
{
// Do State Work
...
if(transition == FROM_STATE_2) {
// New state when doing STATE 2 -> STATE 2
}
if(transition == FROM_STATE_1) {
// New State when moving STATE 1 -> STATE 2
}
return new_state;
}
Есть ли лучший способ для простых конечных автоматов
РЕДАКТИРОВАТЬ: Для С ++ я думаю, что библиотека Boost Statechart может быть подходящим вариантом. Однако это не помогает с C. Давайте сконцентрируемся на сценарии использования C.
c
design-patterns
finite-automata
Benoit
источник
источник
Ответы:
Я предпочитаю использовать табличный подход для большинства конечных автоматов:
Это, конечно, может быть расширено для поддержки нескольких конечных автоматов и т. Д. Также могут быть выполнены действия перехода:
Подход, основанный на таблицах, легче поддерживать и расширять, а также проще отображать на диаграммах состояний.
источник
Возможно, вы видели мой ответ на другой вопрос C, где я упоминал FSM! Вот как я это делаю:
Со следующими определенными макросами
Его можно изменить в зависимости от конкретного случая. Например, у вас может быть файл,
FSMFILE
которым вы хотите управлять своим FSM, поэтому вы можете включить действие чтения следующего символа в сам макрос:теперь у вас есть два типа переходов: один переходит в состояние и читает новый символ, другой переходит в состояние без использования ввода.
Вы также можете автоматизировать обработку EOF с помощью чего-то вроде:
Преимущество этого подхода в том, что вы можете напрямую преобразовать нарисованную вами диаграмму состояний в рабочий код и, наоборот, вы можете легко нарисовать диаграмму состояний из кода.
В других методах реализации конечного автомата структура переходов скрыта в управляющих структурах (в то время как, если, переключение ...) и контролируется значением переменных (типично
state
переменной), и связать красивую диаграмму с диаграммой может оказаться сложной задачей. запутанный код.Я узнал об этой технике из статьи, опубликованной в большом журнале «Computer Language», который, к сожалению, больше не издается.
источник
Я также использовал табличный подход. Однако есть накладные расходы. Зачем хранить второй список указателей? Функция в C без () является константным указателем. Итак, вы можете:
Конечно, в зависимости от вашего фактора страха (например, безопасности или скорости) вы можете проверить наличие действительных указателей. Для конечных автоматов, имеющих более трех состояний, описанный выше подход должен содержать меньше инструкций, чем эквивалентный подход с переключением или таблицей. Вы даже можете создать макрос как:
Кроме того, я чувствую из примера OP, что есть упрощение, которое должно быть сделано при размышлении о / разработке конечного автомата. Я не считаю, что переходное состояние следует использовать для логики. Каждая функция состояния должна иметь возможность выполнять данную роль без явного знания прошлого состояния (состояний). В основном вы проектируете, как перейти из состояния, в котором вы находитесь, в другое состояние.
Наконец, не начинайте проектирование конечного автомата, основанного на «функциональных» границах, используйте для этого подфункции. Вместо этого разделите состояния в зависимости от того, когда вам придется подождать, пока что-то произойдет, прежде чем вы сможете продолжить. Это поможет минимизировать количество запусков конечного автомата, прежде чем вы получите результат. Это может быть важно при написании функций ввода-вывода или обработчиков прерываний.
Также несколько плюсов и минусов классического оператора switch:
Плюсы:
Минусы:
Обратите внимание на два атрибута: за и против. Я думаю, что переключение дает возможность слишком много делиться между состояниями, и взаимозависимость между состояниями может стать неуправляемой. Однако для небольшого числа состояний он может быть наиболее читаемым и удобным в обслуживании.
источник
Для простого конечного автомата просто используйте оператор switch и тип enum для своего состояния. Сделайте свои переходы внутри оператора switch на основе вашего ввода. В реальной программе вы, очевидно, изменили бы «if (input)», чтобы проверять точки перехода. Надеюсь это поможет.
источник
В книге Мартина Фаулера UML Distilled он заявляет (без каламбура) в главе 10 Диаграммы конечных автоматов (выделено мной):
Воспользуемся упрощенным примером состояний дисплея мобильного телефона:
Вложенный переключатель
Фаулер привел пример кода C #, но я адаптировал его к своему примеру.
Государственный образец
Вот реализация моего примера с шаблоном состояния GoF:
Таблицы состояний
Вдохновленный Фаулером, вот таблица для моего примера:
сравнение
Вложенный переключатель хранит всю логику в одном месте, но код может быть трудночитаемым при большом количестве состояний и переходов. Возможно, это более безопасно и легче проверяется, чем другие подходы (без полиморфизма или интерпретации).
Реализация паттерна состояния потенциально распространяет логику на несколько отдельных классов, что может затруднить понимание ее в целом. С другой стороны, небольшие классы легко понять по отдельности. Дизайн становится особенно хрупким, если вы изменяете поведение, добавляя или удаляя переходы, поскольку они являются методами в иерархии и в коде может быть много изменений. Если вы живете по принципу дизайна небольших интерфейсов, вы увидите, что этот шаблон на самом деле не так хорош. Однако если конечный автомат стабилен, то такие изменения не понадобятся.
Подход с использованием таблиц состояний требует написания какого-либо интерпретатора для контента (это может быть проще, если у вас есть отражение на языке, который вы используете), что может потребовать много работы заранее. Как указывает Фаулер, если ваша таблица отделена от вашего кода, вы можете изменить поведение своего программного обеспечения без перекомпиляции. Однако это имеет некоторые последствия для безопасности; программа работает в зависимости от содержимого внешнего файла.
Изменить (не совсем для языка C)
Также существует подход с плавным интерфейсом (также известный как внутренний предметно-ориентированный язык), которому, вероятно, способствуют языки, имеющие первоклассные функции . Библиотека Stateless существует, и в этом блоге показан простой пример с кодом. Реализация Java (предварительно Java8) обсуждается. Мне также показали пример Python на GitHub .
источник
есть также логическая сетка, которая более удобна в обслуживании, поскольку конечный автомат становится больше
источник
В простых случаях вы можете использовать свой метод стиля переключения. Раньше я обнаружил, что хорошо работает и с переходами:
Я ничего не знаю о библиотеке boost, но этот подход очень прост, не требует каких-либо внешних зависимостей и легко реализуется.
источник
switch () - это мощный и стандартный способ реализации конечных автоматов в C, но он может снизить ремонтопригодность, если у вас большое количество состояний. Другой распространенный метод - использовать указатели на функции для сохранения следующего состояния. Этот простой пример реализует триггер установки / сброса:
источник
Я нашел действительно изящную реализацию автомата Мура на языке C на курсе edx.org Embedded Systems - Shape the World UTAustinX - UT.6.02x, глава 10, авторства Джонатана Вальвано и Рамеша Йеррабалли ....
источник
Возможно, вы захотите изучить программное обеспечение генератора libero FSM. Из языка описания состояний и / или редактора диаграмм состояний (Windows) вы можете сгенерировать код для C, C ++, java и многих других ... плюс красивую документацию и диаграммы. Исходный код и двоичные файлы от iMatix
источник
Эта статья хороша для шаблона состояния (хотя это C ++, а не конкретно C).
Если вы можете взять книгу « Шаблоны проектирования в первую очередь », объяснение и пример будут очень ясными.
источник
Один из моих любимых паттернов - паттерн государственного проектирования. Реагировать или вести себя по-разному на один и тот же заданный набор входных данных.
Одна из проблем с использованием операторов switch / case для конечных автоматов заключается в том, что по мере того, как вы создаете больше состояний, switch / case становится все труднее / громоздко для чтения / обслуживания, способствует неорганизованному спагетти-коду и все труднее изменить, не сломав что-то. Я считаю, что использование шаблонов проектирования помогает мне лучше организовать мои данные, и в этом весь смысл абстракции. Вместо того, чтобы разрабатывать код состояния на основе того, из какого состояния вы пришли, вместо этого структурируйте свой код так, чтобы он записывал состояние, когда вы входите в новое состояние. Таким образом, вы фактически получите запись своего предыдущего состояния. Мне нравится ответ @JoshPetit, и я продвинул его решение еще на один шаг, взятый прямо из книги GoF:
stateCtxt.h:
stateCtxt.c:
statehandlers.h:
statehandlers.c:
Для большинства государственных машин, особенно. Конечные автоматы, каждое состояние будет знать, каким должно быть его следующее состояние, а также критерии перехода к следующему состоянию. Для проектов со свободным состоянием это может быть не так, поэтому есть возможность предоставить API для перехода между состояниями. Если вам нужно больше абстракции, каждый обработчик состояния может быть выделен в отдельный файл, который эквивалентен конкретным обработчикам состояния в книге GoF. Если ваш дизайн прост и содержит всего несколько состояний, то для простоты и stateCtxt.c, и statehandlers.c можно объединить в один файл.
источник
По моему опыту, использование оператора switch - это стандартный способ обработки нескольких возможных состояний. Хотя я удивлен, что вы передаете значение перехода для обработки каждого состояния. Я думал, что весь смысл конечного автомата в том, что каждое состояние выполняет одно действие. Затем следующее действие / ввод определяет, в какое новое состояние перейти. Поэтому я ожидал, что каждая функция обработки состояния немедленно выполнит все, что зафиксировано для входа в состояние, а затем решит, нужен ли переход в другое состояние.
источник
Существует книга « Практические диаграммы состояний на C / C ++» . Тем не менее, это путь слишком тяжеловес за то , что нам нужно.
источник
Что касается компилятора, который поддерживает
__COUNTER__
, вы можете использовать их для простых (но больших) машин состояний.Преимущество использования
__COUNTER__
вместо жестко закодированных чисел заключается в том, что вы можете добавлять состояния в середине других состояний, не перенумеровывая каждый раз все. Если компилятор не поддерживает__COUNTER__
, ограниченным образом его можно использовать с осторожностью__LINE__
источник
__COUNTER__
исключает необходимость перенумеровать, потому что прекомпилятор выполняет нумерацию во время компиляции.Вы можете использовать минималистичную структуру конечного автомата UML в c. https://github.com/kiishor/UML-State-Machine-in-C
Он поддерживает как конечный, так и иерархический конечный автомат. В нем всего 3 API, 2 структуры и 1 перечисление.
Государственный автомат представлен
state_machine_t
структурой. Это абстрактная структура, которую можно унаследовать для создания конечного автомата.Состояние представлено указателем на
state_t
структуру в структуре.Если структура настроена для конечного автомата, то
state_t
содержит,Платформа предоставляет API
dispatch_event
для отправки события в конечный автомат и два API для обхода состояния.Для получения дополнительных сведений о том, как реализовать иерархический конечный автомат, обратитесь к репозиторию GitHub.
примеры кода
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in -C / блоб / ведущий / демо / simple_state_machine_enhanced / readme.md
источник
В C ++ рассмотрите шаблон состояния .
источник
Ваш вопрос похож на "есть ли типичный шаблон реализации базы данных"? Ответ зависит от того, чего вы хотите достичь? Если вы хотите реализовать более крупный детерминированный конечный автомат, вы можете использовать модель и генератор конечного автомата. Примеры можно посмотреть на сайте www.StateSoft.org - SM Gallery. Януш Добровольски
источник
Boost имеет библиотеку диаграмм состояний. http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html
Однако я не могу говорить о его использовании. Сам не использовал (пока)
источник