Вот она: Boost coroutines instead of state machines? Maybe... Очень интересные выводы в конце этой статьи сделаны. Имхо, желающим использовать короутины в продакшене имеет смысл ознакомиться и со статьей, и с выводами.
Я же, в силу своего темного прошлого, все-таки остаюсь приверженцем подхода на основе конечных автоматов. Да, кода приходится писать чуть больше. Зато потом нужно меньше голову ломать.
Ну и еще об одной вещи касательно конечных автоматов. В статье показан, на мой взгляд, самый корявый способ их описания: через enum и один большой switch. Между тем, в C++ на указателях можно писать более понятные и удобные в сопровождении конечные автоматы, например:
class state_machine_parser { private: char m_message[255]; uint8_t m_message_length = 0; uint8_t m_message_offset = 0; void (state_machine_parser::*m_stage)( char ) = &state_machine_parser::message_header; public: void process(char c) { (this->*m_stage)( c ); } bool complete() const { return m_stage == &state_machine_parser::message_complete; } private : void message_header( char c ) { m_message_length = c; if( m_message_length == 0 ) { m_stage = &state_machine_parser::message_complete; } else { m_message_offset = 0; m_stage = &state_machine_parser::message_body; } } void message_body( char c ) { m_message[m_message_offset++] = c; if (m_message_offset == m_message_length) { // do something with the message m_stage = &state_machine_parser::message_header; } } void message_complete( char c ) {} }; |
Кстати говоря, на основе указателей можно строить и более сложные автоматы, в которых нужно иметь разные обработчики для разных типов входных воздействий. Например, у вас могут быть входные воздействия вроде SPACE, PLUS, MINUS, DIGIT, POINT, EXPONENT. И состояния EMPTY, SIGN, INT_PART, WAIT_FRACTION, FRACT_PART. Ну и, соответственно, разные типы реакций на сочетания текущего состояния и входного воздействия. Например: (EMPTY+SPACE) -- ничего не делаем, остаемся в состоянии EMPTY. (EMPTY+PLUS) или (EMPTY+MINUS) -- обрабатываем знак и переходим в SIGN. (EMPTY+DIGIT) -- обрабатываем первую цифру целой части числа и переходим в INT_PART...
Все это дело можно оформить в виде матрицы, где строками будут состояния, а столбцами -- входные воздействия. Элементами матрицы -- указатели на функции с соответствующей реакцией. Причем, для развесистых деревьев состояний не обязательно формировать матрицу переходов в С++коде вручную. Зачастую возможно описать несколько вспомогательных структур или функций, которые получат на вход последовательность триплетов (state, input, action), а на выходе дадут нужного размера матрицу.
Так же не могу не отметить, что на мой взгляд, удобство конечных автоматов начнет сказываться, если в каждом состоянии у вас будет несколько возможных входных воздействий. Но об этом уже говорилось несколько ранее.
Комментариев нет:
Отправить комментарий