Functor front-end является предпочтительным интерфейсом на данный момент. Он более мощный, чем стандартный интерфейс, и имеет более читаемую таблицу переходов. Это также облегчает повторное использование частей государственных машин. Как и eUML, он также имеет много предопределенных действий. Фактически, eUML генерирует фронт-энд функтора через Boost. Типа и повышения. Таким образом, оба предлагают одинаковую функциональность.
Ряды, которые MSM предлагал в предыдущем интерфейсе, имеют разные вкусы. Мы видели a_row, g_row, _row, row, не считая внутренних строк. Это уже много нужно знать, так зачем определять новые строки? У этих типов есть некоторые недостатки:
Это больше информации, чем мы хотели бы. Это означает синтаксический шум и многое другое.
Функциональные указатели странные на C++.
Сигнатура действия/охраны ограничена и не допускает дополнительных вариаций параметров (состояние источника, целевое состояние, машина текущего состояния и т.д.)
Нелегко повторно использовать код действия от машины состояния к другой.
Transition table
Мы можем изменить определение простой таблицы перехода учебника на:
Переходы теперь типа «Row» с ровно 5 шаблонными аргументами: состояние источника, событие, состояние цели, действие и охрана. Везде, где нет ничего (например, действий и охранников), напишите «ничего». Действия и охрана — это уже не методы, а функторы, получающие в качестве аргументов обнаруженное событие, машину состояния, исходное и целевое состояние:
Преимущество функторов по сравнению с функциями заключается в том, что функторы являются общими и многоразовыми. Они также позволяют передавать больше параметров, чем просто события. Охранные функторы одинаковы, но у них есть оператор (), возвращающий бул.
Также можно смешать ряды с разных фронтендов. Чтобы показать это, в переходной таблице остался g_row. Примечание: в случае использования функтора действия в таблице перехода машины состояния, содержащейся внутри машины состояния верхнего уровня, параметр “fsm” относится к машине состояния нижнего уровня (ссылаясь на это действие), а не к машине верхнего уровня.
Чтобы проиллюстрировать точку многоразового использования, MSM поставляется с целым набором предопределенных функторов. Пожалуйста, обратитесь к eUML для полного списка . Например, теперь мы собираемся заменить первое действие последовательностью действия, а охрану более сложным функтором.
Мы решили, что теперь хотим выполнить два действия в первом переходе (Stopped - > Playing). Нам нужно только изменить действие start_playback на
и теперь будет выполнять некоторое_действие и запускать_воспроизведение при каждом переходе. ActionSequence_ является функтором, который вызывает каждое действие mpl::vector в последовательности.
Мы также хотим заменить good_disk_format условием типа: “good_disk_format && (some_condition | | some_other_condition)”. Мы можем достичь этого с помощью And_ и Or_ функторов:
Он начинает выглядеть как функциональное программирование. MSM поставляется с функторами для операторов, использованием государственных машин, алгоритмами STL или методами контейнеров.
Defining states with entry/exit actions
Вы, наверное, заметили, что мы просто показали другую таблицу переходов и что мы даже смешали ряды с разных фронтендов. Это означает, что вы можете сделать это и оставить определения для государств без изменений. Большинство примеров делают это, поскольку это самое простое решение. Вы по-прежнему наслаждаетесь простотой первого интерфейса с расширенными возможностями новых типов переходов. Этот tutorial, адаптированный из предыдущего примера, делает именно это.
Конечно, можно также определить состояния, в которых действия входа и выхода также предоставляются в качестве функторов, поскольку они генерируются eUML, и оба интерфейса эквивалентны. Например, мы можем определить состояние как:
struct Empty_Entry
{
template <class Event,class Fsm,class State>
void operator()(Event const&,Fsm&,State&)
{
...
}
}; // same for Empty_Exit
struct Empty_tag {};
struct Empty : public msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit>{};
Это также означает, что вы можете, как и в таблице переходов, писать действия входа / выхода из более сложных комбинаций действий. Поэтому предыдущий пример может быть переписан .
Обычно, однако, вы, вероятно, будете использовать стандартное определение состояния, поскольку оно обеспечивает те же возможности, что и это определение состояния на переднем конце, если вам не нужны некоторые из отгруженных предопределенных функторов или не является поклонником функционального программирования.
What do you actually do inside actions / guards (Part 2)?
Используя базовый интерфейс, мы увидели, как передавать данные действиям через событие, чтобы данные, общие для всех состояний, могли храниться в машине состояний, соответствующие состояниям данные могли храниться в состоянии и получать доступ в качестве параметра шаблона в действиях входа / выхода. Однако отсутствовала возможность доступа к соответствующим государственным данным в процессе перехода. Это возможно с помощью этого интерфейса. В качестве аргументов приводятся также исходное и целевое состояние переходного периода. Если бы состояние текущего расчета было найдено в исходном состоянии перехода (что бы это ни было), мы могли бы получить к нему доступ:
Как и состояния, машины состояний могут быть определены с использованием предыдущего интерфейса, как показано в предыдущем примере, или с интерфейсом фанктора, который позволяет определить функции входа и выхода машины состояний как функторы, как в этом примере .
Anonymous transitions
Анонимные (завершенные) переходы — это переходы без названного события. Мы видели, как этот интерфейс использует none, когда не требуется никаких действий или охраны. Мы также можем использовать none вместо события, чтобы отметить анонимный переход. Например, следующий переход делает немедленный переход от государства 1 к государству 2:
Row < State1 , none , State2 >
Следующий переход делает то же самое, но вызывает действие в процессе:
На следующей диаграмме показан пример и его реализация:
Internal
transitions
В следующем примере используются внутренние переходы с фронтальным функтором. Что касается простого стандартного интерфейса, то поддерживаются оба метода определения внутренних переходов:
предоставление Row в таблице перехода машины состояния с none в качестве целевого состояния определяет внутренний переход.
предоставление internal_transition_table из строк Internal внутри состояния или подмашины определяет UML-конформные внутренние переходы с более высоким приоритетом.
Переходы, определенные внутри internal_transition_table, не требуют состояния источника или целевого состояния, поскольку состояние источника известно (Internal на самом деле Row без состояния источника или целевого состояния).
Как и для стандартных внутренних переходов переднего конца , внутренние таблицы переходов добавляются в таблицу машины основного состояния, что позволяет распределить определение таблицы переходов и повторно использовать состояния.
Для подмашин предлагается дополнительный бонус, который может иметь как стандартную таблицу переходов, так и внутреннюю таблицу переходов (которая имеет более высокий приоритет). Это упрощает задачу, если вы решите сделать полноценную машину из штата позже. Это также несколько быстрее, чем стандартная альтернатива, с добавлением ортогональных регионов, поскольку отправка событий, если она будет принята внутренней таблицей, не будет продолжаться в субрегионах. Это дает вам отправку O(1) вместо O(количество регионов). В то время как пример с eUML, то же самое возможно с этим интерфейсом.
Kleene (any) event
Как правило, МСМ требует события, чтобы запустить переход. Но бывают случаи, когда любое событие, независимо от того, какое из них произошло бы:
Если вы хотите уменьшить количество переходов: любое событие будет делать, возможно, охранники решат, что произойдет
Состояния входа в псевдосообщество не обязательно хотят знать событие, вызвавшее их активацию, или они могут хотеть знать только его свойство.
MSM поддерживает повышение: любое событие. Это событие будет соответствовать любому событию, а это означает, что если переход с ускорением: любое событие происходит из текущего состояния, этот переход будет возгораться (при отсутствии охраны или переходе с более высоким приоритетом). Это событие называется Kleene, как вершина отсчета звезды Kleene, используемой в регексе.
Например, этот переход на экземпляре машины состояния под названием fsm:
Row < State1, boost::any, State2>
будет стрелять, если State1 активен и событие обрабатывается:
fsm.process_event(whatever_event());
На этом этапе вы можете использовать это событие any в переходных действиях, чтобы вернуться к исходному событию, позвонив, например, boost::any::type().
Также можно поддержать свои собственные события Kleene, специализируясь на ускорении::msm:::is_kleene_event для данного события, например:
Единственным требованием является то, что на этом событии должен быть конструктор копий с события, первоначально обработанного на государственной машине.
Статья Functor front-end раздела Meta State Machine (MSM) Chapter 3. Tutorial может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.