Я использовал его на другом языке программирования, и это очень полезно.
Я не могу найти ничего об этом для C ++.
Давайте, например, возьмем следующий код:
void change();
enum
{
end = 0,
gmx
}
int
gExitType;
int main()
{
gExitType = end;
SetTimer(&change, 10000, 0);
return 0;
}
void ApplicationExit()
{
switch (gExitType)
{
case end:
printf("This application was ended by the server");
case gmx:
printf("This application was ended by the timer");
}
::exit(0);
}
void change()
{
gExitType = gmx;
ApplicationExit();
}
Это как то, как мы бы это делали в C ++, но при использовании конечного автомата / автоматов я мог бы сделать что-то подобное на другом языке:
void change();
int main()
{
state exitType:end;
SetTimer(&change, 10000, 0);
return 0;
}
void ApplicationExit() <exitType:end>
{
printf("This application was ended by the server");
}
void ApplicationExit() <exitType:gmx>
{
printf("This application ended by the timer");
}
void change()
{
state exitType:gmx;
ApplicationExit();
}
По моему мнению, это действительно элегантный способ достижения цели.
Как бы я сделал это в C ++? Этот код, кажется, не работает (очевидно, поскольку я не могу найти ничего автоматов, связанных с C ++)
Чтобы уточнить мое мнение:
Так каковы преимущества использования этой техники? Ну, как вы можете ясно видеть, код меньше; Конечно, я добавил перечисление в первую версию, чтобы сделать примеры более похожими, но функции ApplicationExit явно меньше. Это также намного более явно — вам не нужны большие операторы switch в функциях для определения того, что происходит, если вы хотите, вы можете поместить разные ApplicationExits в разные файлы для независимой обработки разных наборов кода. Он также использует меньше глобальных переменных.
В этом конкретном примере вы можете использовать объекты и полиморфизм для представления различных состояний. Например:
class StateObject
{
public:
virtual void action(void) = 0;
};
class EndedBy : public StateObject
{
private:
const char *const reason;
public:
EndedBy( const char *const reason_ ) : reason( reason_ ) { }
virtual void action(void)
{
puts(reason);
}
};
EndedBy EndedByServer("This application was ended by the server");
EndedBy EndedByTimer ("This application ended by the timer");
StateObject *state = &EndedByServer;
void change()
{
state = &EndedByTimer;
}
void ApplicationExit()
{
state->action();
::exit(0);
}
int main()
{
SetTimer(&change, 10000, 0);
// whatever stuff here...
// presumably eventually causes ApplicationExit() to get called before return 0;
return 0;
}
Тем не менее, это не отличный дизайн, и это не FSM в общем смысле. Но это осуществит вашу насущную потребность.
Вы можете посмотреть шаблон состояния (одна ссылка: http://en.wikipedia.org/wiki/State_pattern ) для более общего рассмотрения этого паттерна.
Однако основная идея заключается в том, что каждое состояние является подклассом некоторого общего класса «состояний», и вы можете использовать полиморфизм для определения различных действий и поведений, представленных каждым состоянием. Указатель на общий базовый класс «state» отслеживает состояние, в котором вы находитесь в данный момент.
Объекты состояния могут быть разных типов или, как в моем примере выше, разные экземпляры одного и того же объекта, настроенные по-разному, или смесь.
Существуют библиотеки C ++, такие как Boost.statechart, которые специально пытаются обеспечить расширенную поддержку конечных автоматов кодирования:
http://www.boost.org/doc/libs/1_54_0/libs/statechart/doc/tutorial.html
Помимо этого, один очень элегантный способ кодирования определенных типов конечных автоматов состоит в том, чтобы определить их как программу:
http://c2.com/cgi/wiki?CoRoutine
http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/
Сопрограммы напрямую не поддерживаются в C ++, но есть два возможных подхода к
реализуя их:
1) Использование техники, аналогичной реализации устройства Даффа, подробно объяснено здесь:
http://blog.think-async.com/search/label/coroutines
Это очень похоже на то, как работают итераторы C #, например, и одно ограничение состоит в том, что получение сопрограммы может быть сделано только из самой верхней функции в стеке вызовов сопрограммы. OTOH, преимущество этого метода в том, что для каждого экземпляра сопрограммы требуется очень мало памяти.
2) Выделение отдельного стека и регистрация места для каждой сопрограммы.
По сути, это делает сопрограмму полноценным потоком выполнения с той лишь разницей, что пользователь несет полную ответственность за планирование потоков (также известное как совместная многозадачность).
Портативная реализация доступна от boost:
http://www.boost.org/doc/libs/1_54_0/libs/coroutine/doc/html/coroutine/intro.html
Вы можете использовать специализацию значений шаблона над int, чтобы достичь почти того, что вы хотите.
(Извините, я на своем планшете, поэтому не могу привести пример, обновлю в воскресенье)