Массив enum
- Войдите на сайт для отправки комментариев
Втр, 25/02/2014 - 17:14
Есть перечисление ID (состояний) устройства:
enum states { CLEAN, DRIVE, SENSORS, EFFECT1, EFFECT2, EFFECT3};
И есть соответствующие им сообщения:
const char *menuPage1[]=
{
"Clean plate",
"Working...",
"Sensor settings",
"Effect 1",
"Effect 2",
"Effect 3"
};Сейчас чтобы что-то подправить, надо аккуратно сличать количество и последовательность пунктов.
Есть ли способ их объединить в одну структуру/массив/что-то еще, чтобы было наглядное соответствие ID обозначения?
Т.е. что-то вроде этого:
{
{ CLEAN, "Clean plate" }
{ DRIVE, "Working..." }
{ SENSORS, "Sensor settings" }
{ EFFECT1, "Effect 1" }
{ EFFECT2, "Effect 2" }
{ EFFECT3, "Effect 3" }
};
typedef struct { enum states state; const char *str; } menu_t; menu_t pages[] = { { CLEAN, "Clean plate" } { DRIVE, "Working..." } { SENSORS, "Sensor settings" } { EFFECT1, "Effect 1" } { EFFECT2, "Effect 2" } { EFFECT3, "Effect 3" } };Примерно так
Спасибо.
А как ID теперь использовать в switch...case?
Раньше было switch (states) {case CLEAN: ... }
Добавить функцию, которая по состоянию будет искать в массиве соответствующий текст и возвращать указатель на этот текст. Универсально, при добавлении нового состояния, функция будет работать
плохая реализация, лучше строки сделать как переменные.
ну а потом уже много if
if( !strcmp( CLEAN_STR, text ) ) return CLEAN; else if( .... )плохая реализация, лучше строки сделать как переменные.
ну а потом уже много if
if( !strcmp( CLEAN_STR, text ) ) return CLEAN; else if( .... )как я понял, но ему нужно сравнивать с ID, те те перебирать в цикле все элементы
pages[i].state для i=0; i = maxindex
и выразить maxindex через sizeof или SIZEOF ???
typedef struct { enum states state; const char *str; } menu_t; menu_t pages[] = { { CLEAN, "Clean plate" } { DRIVE, "Working..." } { SENSORS, "Sensor settings" } { EFFECT1, "Effect 1" } { EFFECT2, "Effect 2" } { EFFECT3, "Effect 3" } };Как это сейчас:
switch (stateID) // в зависимости от ID формируем информационные экраны { case DRIVE: lcd.setCursor(0,0); lcd.print("Working..."); lcd.setCursor(2,1); lcd.print("Coordinates: "); printToLcd(xyPosition); lcd.setCursor(2,2); lcd.print("Speed: "); lcd.print(currentSpeed); lcd.setCursor(2,3); lcd.print("Time left: "); lcd.print(remainingTime); break; case CLEAN: ...И этот заголовок "Working...", выводимый по текущему состоянию DRIVE, используется еще в одном месте. Т.е. чтобы что-то подправить, приходится прыгать по трем кускам кода . Было бы удобнее, если их объявление будет легкочитаемо и в одном месте, как предложил ites. И далее программно только выбирать через switch нужные строки. Вот только как синтаксически выразить в case элемент DRIVE из menu_t pages[] (если по примеру из сообщения #2) - я пока не знаю.
И далее программно только выбирать через switch нужные строки. Вот только как синтаксически выразить в case элемент DRIVE из menu_t pages[] (если по примеру из сообщения #2) - я пока не знаю.
для swich нужны метки=константы,
можно case A:
и нельзя A[i]: // но если const int A[]={const1, const2 ...}; ?
После применения структуры доступны элементы массива, те в общем случае переменные. Enum в действительности перечислчяемые константы, но поймет ли компилятор как константу pages[i].state ?
Если не поймет, попытаться обойти при помощи препроцессорных директив?
спросите лучше у maksim
ну есть же в сети сотни тысяч класических реализаций....
Вам нужно только 2 функции и никаких массивов или других конструкций ибо будет неудобно и не универсально.
typedef enum { .... } XXX: const char * EnumXXX_toStr( XXX value ); XXX EnumXXX_ToValue( const char * text );А далее пользуйтесь этим всем без заморочек где угодно и сколько угодно раз.
if( !strcmp( CLEAN_STR, text ) ) return CLEAN; else if( strcmp(.....) ) .....Хотя тут есть очень спорные моменты, ибо этот код проверят полное совпадение, если вам надо без учета регистра то придется добавить чуть кода
Ну перевод значения в строку это совсем просто, это либо ваш массив, но я бы просто сделал switch...case, будет просто несколько гибче.
З.Ы. Я только никак не могу понять зачем вам строки переводить в значения ? Обычно такое возникает только при общении с внешним миром по текстовому протоколу, но тогда это будет самая маленькая проблема, и вы на разборе текста огребете столько проблем.
мне не нужно строки переводить в значения (в enum как раз не строки, а числовые последовательные значения: CLEAR это синоним 0, DRIVE = 1 и т.д.). Через enum и case это легко читается в коде и обратывается проходом в цикле при необходимости. Через if уже менее наглядно.
Мне нужно: если текущее состояние DRIVE (или 1, т.к. в enum они все равно пронумерованы), вывести в первой строке соответствующий заголовок. Т.к. это состояние и заголовок не меняются в трех разных функциях, хочется не прописывать одно и то же в трех местах, а ради удобства просто держать связку "состояние, заголовок" визуально рядом (в какой-то конструкции) и тупо в тех функциях: "Если состояние А1, показать строку А2". А знаний пока не хватает...
Не понимаю - изначально сделали массив структур для упорядочиввания и автоматического соответствия данных, а также во избежание ошибок.
Но воспользоваться enum значениями элементов массива в операторе switch-case не можете. В чем суть темы или цель по ходу действия поменялась ?
увы, "массив структур" пока не удалось сделать
есть отдельно
enumstatesи отдельноconstchar*menuPage1[]Если их соединить в
typedefstruct{02enumstates state;03constchar*str;04} menu_t;то как в case использовать элементы из нее? На любые варианты компилятор гругается.
Вот проверил, работает, но определение enum в одном файле с функцией с параметром типа enum приводит к ошибке компиляции - баг уже описывали тут, пришлось enum описать в хидерном файле enum.h и все заработало (писал для тестирования, не пинать за огрехи):
//enum state_t {CLEAN, DRIVE, SENSORS, EFFECT1, EFFECT2, EFFECT3}; -- это поместить в файл enum.h #include "enum.h" typedef struct { state_t state; const char *str; } menu_t; menu_t menu[] = { { CLEAN, "Clean plate" }, { DRIVE, "Working..." }, { SENSORS, "Sensor settings" }, { EFFECT1, "Effect 1" }, { EFFECT2, "Effect 2" }, { EFFECT3, "Effect 3" } }; byte get_menu_num(state_t state) { for (byte i = 0; i < sizeof(menu)/sizeof(menu_t); i++) if (menu[i].state == state) return i; return 0; // ACHTUNG! } // int get_menu_num() void setup() { Serial.begin(57600); } // void setup() void loop() { static state_t stateID = CLEAN; while (!Serial.available()); stateID = (state_t)(Serial.read()-'0'); switch (stateID) { // в зависимости от ID формируем информационные экраны // в case пишем свой код case DRIVE: Serial.println(menu[get_menu_num(stateID)].str); break; case CLEAN: Serial.println(menu[get_menu_num(stateID)].str); break; case EFFECT1: Serial.println(menu[get_menu_num(stateID)].str); break; case EFFECT2: Serial.println(menu[get_menu_num(stateID)].str); break; case EFFECT3: Serial.println(menu[get_menu_num(stateID)].str); break; default: Serial.println('?'); } // switch (stateID) } // void loop()жесть.... просто нереальная жесть.... зачем все эти танцы с бубуном....
зачем городить такой огород с поиском.
typedef enum { CLEAN, DRIVE, SENSORS, EFFECT1, EFFECT2, EFFECT3 } States; const char * StatesToStr( States state ) { switch (state) { case DRIVE: return "DRIVE"; case CLEAN: return "CLEAN"; case EFFECT1: return "EFFECT1"; case EFFECT2: return "EFFECT2"; case EFFECT3: return "EFFECT3"; default: return "Onknown"; } } void loop() { States state; ............... ............... Serail.Print( "Current state is " ); Serail.Print( StatesToStr( state ) ) }Я это не компилил, сейчас просто не могу, но думаю что даже enum скомпилиться без проблем :-) в ваших примерах похоже typedef используется некооректно.
Вся жесть для того, чтобы протестить первый предложенный ответ, а заодно ответить на последний вопрос автора. Да мне и самому захотелось изучить эти конструкции :)
Вся жесть для того, чтобы протестить первый предложенный ответ, а заодно ответить на последний вопрос автора. Да мне и самому захотелось изучить эти конструкции :)
О да месье знает толк в извращениях :-)
главная идея была в автоматизации соответствия текущего state и соответствующего указателя на строку
используя то, что state являются перечисляемыми со значениями 0,1,2,3 ...., можно использовать state как индекс массива, код не проверял и возможно придется сделать явное преобразование типа (int)state, в идеале switch-case сведется к одной строчке: return (pages[state].str);
typedef struct { enum states state; const char *str; } menu_t; menu_t pages[] = { { CLEAN, "CLEAN" } { DRIVE, "DRIVE" } { SENSORS, "SENSORS" } { EFFECT1, "EFFECT1" } { EFFECT2, "EFFECT2" } { EFFECT3, "EFFECT3" } }; void setup() { //............. } void loop() { //............. } const char * StatesToStr( States state ) {return (pages[state].str);}главная идея была в автоматизации соответствия текущего state и соответствующего указателя на строку
используя то, что state являются перечисляемыми со значениями 0,1,2,3 ...., можно использовать state как индекс массива, код не проверял и возможно придется сделать явное преобразование типа (int)state, в идеале switch-case сведется к одной строчке: return (pages[state].str);
Ну и пришли вы к тому, с чего начали - enum и и массив строк ! :)
Только там неявно номер в массиве соответствовал номеру енум, а теперь просто изменится вид объявления вашего массива строк. А это могли и раньше использовать: return (pages[state]);
ура, меня наконец-то поняли :) радует, что код оптимизируется на глазах.
Всем большое спасибо, сейчас буду разбираться с каждым вариантом.
Вот проверил, работает, но определение enum в одном файле с функцией с параметром типа enum приводит к ошибке компиляции - баг уже описывали тут, пришлось enum описать в хидерном файле enum.h
эта ошибка в компиляторе еще не устранена ?
Такой тест получился:
// TEST enum state_t {CLEAN, DRIVE, SENSORS, EFFECT1, EFFECT2, EFFECT3}; /* const char* menu[] = { "Clean plate", "Working...", "Sensor settings", "Effect 1", "Effect 2", "Effect 3" }; */ char menu[10][32]; void setup () { Serial.begin(57600); // Этого удобства добивались? Чет геморно как-то... strcpy(menu[CLEAN], "Clean plate"); strcpy(menu[DRIVE], "Working..."); strcpy(menu[SENSORS], "Sensor settings"); strcpy(menu[EFFECT1], "Effect 1"); strcpy(menu[EFFECT2], "Effect 2"); strcpy(menu[EFFECT3], "Effect 3"); } // void setup() void loop() { static state_t stateID = CLEAN; while (!Serial.available()); stateID = (state_t)(Serial.read()-'0'); // Жмем кнопки 0 - 5. !ACHTUNG! возможен выход за границы массива! Serial.println(menu[stateID]); } // void loop()С интересом продолжаю наблюдать за развитием темы. Продолжайте... :)
Константы в enum по умолчанию отсчитываются с 0 и увеличиваются на 1. Их можно использовать просто как индекс в массиве, если массив упорядочен. Если не упорядочен, то по нему нужен поиск.
да, так и писать
case CLEAN:
а если в switch предусмотрите формальное преобразование переменной к типу к STATE,
то компилятор должен заметить в "case" любую "инородную" константу, например CLEAN1 не входящую в список enum STATE ; (CLEAN1 может иметь допустимое численное значение 0,1,2,.... , но не вхрдить в нужный список enum)
Такой тест получился:
// TEST enum state_t {CLEAN, DRIVE, SENSORS, EFFECT1, EFFECT2, EFFECT3}; /* const char* menu[] = { "Clean plate", "Working...", "Sensor settings", "Effect 1", "Effect 2", "Effect 3" }; */ char menu[10][32]; void setup () { Serial.begin(57600); // Этого удобства добивались? Чет геморно как-то... strcpy(menu[CLEAN], "Clean plate"); strcpy(menu[DRIVE], "Working..."); strcpy(menu[SENSORS], "Sensor settings"); strcpy(menu[EFFECT1], "Effect 1"); strcpy(menu[EFFECT2], "Effect 2"); strcpy(menu[EFFECT3], "Effect 3"); } // void setup() void loop() { static state_t stateID = CLEAN; while (!Serial.available()); stateID = (state_t)(Serial.read()-'0'); // Жмем кнопки 0 - 5. !ACHTUNG! возможен выход за границы массива! Serial.println(menu[stateID]); } // void loop()Усложняете программу и понижаете ее надежность и читаемость. Берете на себя рутинные функции компилятора - сами определяете размерность массива
charmenu[10][32];Малейшая ошибка при копировании строки, связанная с ее длиной и выходом за пределы допустимого индекса и вы затрете смежную область памяти.
Усложняете программу и понижаете ее надежность и читаемость. Берете на себя рутинные функции компилятора - сами определяете размерность массива
charmenu[10][32];Малейшая ошибка при копировании строки, связанная с ее длиной и выходом за пределы допустимого индекса и вы затрете смежную область памяти.
Так мне то этот гемор не нужен, у меня нет запар с сопоставлением порядковых номеров и строк :)
Все для афтора топика!
У меня все подходы системные. Читайте внимательтно тексты программ, там все сказано и про тестирование и про ожидаемые глюки. И уж простите, но ваше мнение меня совершенно не интересует. Без обид.
вот оно, решение что искалось изначально: http://habrahabr.ru/post/241941/ :)
Привет! Удалось реализовать идею по этой статье с хабра? Можно взглянуть на ардуино-вариант для решения исходной задачи?