Поскольку контейнер vector поддерживает operator [], то для поиска состояния, в которое необходимо совершить переход, в таблице
переходов можно воспользоваться подобной конструкцией:
NewState = Transitions[ EventIndex ] [
StateIndex ];
|
где соответствующие индексы могут быть вычислены с
помощью алгоритма STL distance:
inline int GetStateIndex( const StateType & State ) const
{
return std::distance(
States.begin(), std::find( States.begin(), States.end(), State ) );
}
|
Разумеется, класс автомата должен будет иметь функцию, принимающую и обрабатывающую событие. Существует два варианта. Первый – это
функция, второй – перегрузка какого-либо оператора. Для придания дополнительной
гибкости реализуем оба варианта:
SFiniteStateMachine &
AcceptEvent( const EventType &
Event )
{
. . .
}
|
и
inline
SFiniteStateMachine & operator << ( const EventType & Event )
{ return AcceptEvent( Event ); }
|
Перегрузка operator << даст возможность
использовать автомат в таком стиле:
// Принять события Event1, Event2 и Event3
MyMachine << Event1 <<
Event2 << Event3;
|
Остается вопрос: что делать, если придет событие, для
которого у автомата нет описания переходов? Возможны варианты: просто
проигнорировать такое событие, сгенерировать исключение или сделать что-то, определяемое пользователем. Воспользуемся идеей стратегий ([4]) и включим в
число аргументов шаблона функтор, который будет определять нужную стратегию
поведения. Такой подход вполне соответствует требованию 5. При этом можно
задать стратегию по умолчанию – например, генерировать исключение. Теперь
заголовок шаблона выглядит так:
template
<class SState, class SEvent, class SUnknownEventStrategy =
SThrowStrategy<SEvent> >
class SFiniteStateMachine { . . . };
|
В числе заготовленных стратегий есть и стратегия
игнорирования неизвестного события: