Итак, итоги. Проблема пива сильно преувеличена, все прозрачно. Создание описаний состояний машины состояний с помощью макросов на языке С.
1. Преамбула.
При реализации алгоритма функционирования прибора с помощью машины состояний существует проблема удобной записи параметров состояния. К параметрам обычно относятся:
- Номер состояния
- Вектор обработчика
- Текстовая информация (например, текст выводимый на экран)
- Вспомогательная информация (например, необходимость очистки экрана)
Проблема заключается в том, что, с одной стороны, описания всех параметров состояния должны быть расположены рядом , с другой стороны, параметры должны быть включены в разнородные элементы программы, а с третьей стороны, добавление и удаление состояний и изменение порядка следования должно быть простым.
Так, номер состояния должен включаться в перечисление enum для удобного добавления и удаления состояний, вектора функций обработки состояний должны включаться в массив указателей на функции, текстовая информация должна включаться в массив текстовых строк, вспомогательная информация обычно включается в массив целых значений. Количество состояний машины состояний зависит от сложности процесса и в среднем по функциональности приборе легко достигает сотни.
Задача: не усложняя понимание и поиск ошибок при компиляции, не используя пиво для вхождения в режим прозрачности, выбрать наиболее оптимальный путь реализации.
2. Амбула.
2.1. Составляем список параметров состояния. Допустим, в нашей машине состояний требуется иметь:
- Номер состояния
- Обработчик состояния
- Текст на английском языке
- Текст на немецком языке
2.2. Выбираем название макроса и последовательность перечисления всех параметров
UI_STATE (ui_state, ui_fun, ui_eng, ui_germ)
2.3. Записываем все состояния в одном отдельном файле ui_states.h в виде:
UI_STATE (stRestart ,fRestart ,"Restart" ,"Neustart" )
UI_STATE (stSelfTest ,fSelfTest ,"Self-test" ,"Selbsttest" )
UI_STATE (stWaiting ,fWaiting ,"Ready" ,"Bereit" )
UI_STATE (stError ,fError , "Error" ,"Fehler" )
UI_STATE (stLowPower ,fLowPower , "Low power" ,"Unterspannung" )
UI_STATE (stUserMenu ,fUserMenu ,"extended funct" ,"erweit. Funkt." )
UI_STATE (stBacklight ,fBacklight ,"Backlight" ,"Displ.-Beleucht.")
UI_STATE (stSound ,fSound , "Sound" ,"Signalton" )
UI_STATE (stStatistic ,fViewStat ,"Statistic" ,"Statistik" )
Как видно в предыдущей строке, исходя из некоторых соображений, используется раздельное определение номера состояния и функции обработчика. Второй вариант записи, с помощью единого названия состояния, приведен ниже:
UI_STATE (Restart ,"Restart" ,"Neustart" )
UI_STATE (SelfTest ,"Self-test" ,"Selbsttest" )
UI_STATE (Waiting ,"Ready" ,"Bereit" )
2.4. Далее в нужном месте программы (обычно это заголовочный файл машины состояний) включаем enum:
enum {
#define UI_STATE (ui_state, ui_fun, ui_eng, ui_germ) ui_state ,
#include "ui_states.h"
#undef UI_STATE
stQty
};
Таким образом номера состояний определены.
2.5.Для формирования массива обработчиков используем запись:
typedef void (*VECTORS)();
const VECTORS function[stQty] = {
#define UI_STATE (ui_st, ui_fun, ui_eng, ui_germ) ui_fun ,
#include "ui_states.h"
#undef UI_STATE
};
2.5. Для формирования двумерного массива текстовых строк используем запись:
#define langQty 2
const char * const st_name[langQty][stQty]=
{ {
#define UI_STATE (ui_state, ui_fun, ui_eng, ui_germ) ui_engl,
#include "ui_states.h"
#undef UI_STATE
},{
#define UI_STATE (ui_state, ui_fun, ui_eng, ui_germ) ui_germ,
#include "ui_states.h"
#undef UI_STATE
} };
2.6. Ну и в варианте одного названия состояния и префиксов st и fn соответствующие строки выглядят так:
#define UI_STATE (state, ui_eng, ui_germ) st##state ,
#define UI_STATE (state, ui_eng, ui_germ) fn##state,