Массив enum

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Есть перечисление 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" }
};

 

ites
Offline
Зарегистрирован: 26.12.2013
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" }
};

Примерно так

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Спасибо.
А как ID теперь использовать в switch...case?
Раньше было switch (states) {case CLEAN: ... }

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Добавить функцию, которая по состоянию будет искать в массиве соответствующий текст и возвращать указатель на этот текст. Универсально, при добавлении нового состояния, функция будет работать

gregoryl
Offline
Зарегистрирован: 09.09.2013

плохая реализация, лучше строки сделать как переменные.

const char * CLEAN_str = "Clean plate";

ну а потом уже много if

if( !strcmp( CLEAN_STR, text ) )
    return CLEAN;
else
    if( .... )

 

 
KamovEugeniy
Offline
Зарегистрирован: 24.02.2014

gregoryl пишет:

плохая реализация, лучше строки сделать как переменные.

const char * CLEAN_str = "Clean plate";

ну а потом уже много if

if( !strcmp( CLEAN_STR, text ) )
    return CLEAN;
else
    if( .... )

 

как я  понял, но ему нужно сравнивать с ID, те те перебирать в цикле все элементы

pages[i].state для i=0; i = maxindex  

и выразить maxindex через sizeof или  SIZEOF ???

ites пишет:

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" }
};

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

Как это сейчас:







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) - я пока не знаю.

KamovEugeniy
Offline
Зарегистрирован: 24.02.2014

Tomasina пишет:

 И далее программно только выбирать через switch нужные строки. Вот только как синтаксически выразить в case элемент DRIVE из menu_t pages[] (если по примеру из сообщения #2) - я пока не знаю.

для swich нужны  метки=константы,

можно case A:

и нельзя A[i]:  // но если const int A[]={const1, const2 ...};  ?

После применения структуры доступны элементы массива, те в общем случае переменные. Enum в действительности перечислчяемые константы, но поймет ли компилятор как константу pages[i].state ?

Если не поймет, попытаться обойти при помощи препроцессорных директив?

спросите лучше у maksim

 

gregoryl
Offline
Зарегистрирован: 09.09.2013

ну есть же в сети сотни тысяч класических реализаций....

Вам нужно только 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, будет просто несколько гибче.

З.Ы. Я только никак не могу понять зачем вам строки переводить в значения ? Обычно такое возникает только при общении с внешним миром по текстовому протоколу, но тогда это будет самая маленькая проблема, и вы на разборе текста огребете столько проблем.  

 
Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

мне не нужно строки переводить в значения (в enum как раз не строки, а числовые последовательные значения: CLEAR это синоним 0, DRIVE = 1 и т.д.). Через enum и case это легко читается в коде и обратывается проходом в цикле при необходимости. Через if уже менее наглядно.

Мне нужно: если текущее состояние DRIVE (или 1, т.к. в enum они все равно пронумерованы), вывести в первой строке соответствующий заголовок. Т.к. это состояние и заголовок не меняются в трех разных функциях, хочется не прописывать одно и то же в трех местах, а ради удобства просто держать связку "состояние, заголовок" визуально рядом (в какой-то конструкции) и тупо в тех функциях: "Если состояние А1, показать строку А2". А знаний пока не хватает...

KamovEugeniy
Offline
Зарегистрирован: 24.02.2014

Не понимаю - изначально сделали массив структур для упорядочиввания и  автоматического соответствия данных, а также во избежание ошибок.

Но воспользоваться enum значениями элементов массива в операторе switch-case не можете. В чем суть темы или цель по ходу действия поменялась ?

 

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

увы, "массив структур" пока не удалось сделать

есть отдельно enum states и отдельно const char *menuPage1[] 

Если их соединить в 

typedef struct  {
02   enum states state;
03   const char *str;
04 }  menu_t;

то как в case использовать элементы из нее? На любые варианты компилятор гругается.

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Вот проверил, работает, но определение 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()

 

 

gregoryl
Offline
Зарегистрирован: 09.09.2013

жесть.... просто нереальная жесть.... зачем все эти танцы с бубуном....

зачем городить такой огород с поиском.

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 используется некооректно.

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Вся жесть для того, чтобы протестить первый предложенный ответ, а заодно ответить на последний вопрос автора. Да мне и самому захотелось изучить эти конструкции :)

gregoryl
Offline
Зарегистрирован: 09.09.2013

Garry пишет:

Вся жесть для того, чтобы протестить первый предложенный ответ, а заодно ответить на последний вопрос автора. Да мне и самому захотелось изучить эти конструкции :)

О да месье знает толк в извращениях :-)

KamovEugeniy
Offline
Зарегистрирован: 24.02.2014

главная идея была в автоматизации соответствия  текущего 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);}

 

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

KamovEugeniy пишет:

главная идея была в автоматизации соответствия  текущего state  и соответствующего указателя на строку

используя то, что state являются перечисляемыми со значениями 0,1,2,3 ...., можно использовать state как индекс массива, код не проверял и возможно придется сделать явное преобразование типа (int)state, в идеале switch-case сведется к одной строчке:  return (pages[state].str);

Ну и пришли вы к тому, с чего начали - enum и и массив строк ! :)

Только там неявно номер в массиве соответствовал номеру енум, а теперь просто изменится вид объявления вашего массива строк. А это могли и раньше использовать: return (pages[state]);

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

ура, меня наконец-то поняли :) радует, что код оптимизируется на глазах.
Всем большое спасибо, сейчас буду разбираться с каждым вариантом.

KamovEugeniy
Offline
Зарегистрирован: 24.02.2014

Garry пишет:

Вот проверил, работает, но определение enum в одном файле с функцией с параметром типа enum приводит к ошибке компиляции - баг уже описывали тут, пришлось enum описать в хидерном файле enum.h

эта ошибка в компиляторе еще не устранена ?

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Такой тест получился:

// 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()

 

ites
Offline
Зарегистрирован: 26.12.2013

С интересом продолжаю наблюдать за развитием темы. Продолжайте... :)

Tomasina пишет:
Спасибо. А как ID теперь использовать в switch...case? Раньше было switch (states) {case CLEAN: ... }

Константы в enum по умолчанию отсчитываются с 0 и увеличиваются на 1. Их можно использовать просто как индекс в массиве, если массив упорядочен. Если не упорядочен, то по нему нужен поиск.

Sheet99
Offline
Зарегистрирован: 27.02.2014

Tomasina пишет:
Спасибо. А как ID теперь использовать в switch...case? Раньше было switch (states) {case CLEAN: ... }

да, так и писать

case CLEAN:

а если в switch предусмотрите формальное преобразование переменной к типу к STATE,

то компилятор должен заметить в "case" любую "инородную" константу, например CLEAN1 не входящую в список enum STATE               ; (CLEAN1 может иметь допустимое численное значение 0,1,2,.... , но не вхрдить в нужный список enum)

Garry пишет:

Такой тест получился:

// 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()

 

Усложняете программу и понижаете ее надежность и читаемость. Берете на себя рутинные функции компилятора - сами определяете размерность массива char menu[10][32];

Малейшая ошибка при копировании строки, связанная с ее длиной и выходом за пределы допустимого индекса и вы затрете смежную область памяти.

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Sheet99 пишет:

Усложняете программу и понижаете ее надежность и читаемость. Берете на себя рутинные функции компилятора - сами определяете размерность массива char menu[10][32];

Малейшая ошибка при копировании строки, связанная с ее длиной и выходом за пределы допустимого индекса и вы затрете смежную область памяти.

Так мне то этот гемор не нужен, у меня нет запар с сопоставлением порядковых номеров и строк :)

Все для афтора топика!

Sheet99
Offline
Зарегистрирован: 27.02.2014

Garry пишет:
Так мне то этот гемор не нужен, у меня нет запар с сопоставлением порядковых номеров и строк :) Все для афтора топика!
ваш подход антисистемный

Garry
Garry аватар
Offline
Зарегистрирован: 07.04.2012

Sheet99 пишет:

Garry пишет:
Так мне то этот гемор не нужен, у меня нет запар с сопоставлением порядковых номеров и строк :) Все для афтора топика!
ваш подход антисистемный

У меня все подходы системные. Читайте внимательтно тексты программ, там все сказано и про тестирование и про ожидаемые глюки. И уж простите, но ваше мнение меня совершенно не интересует. Без обид.

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

вот оно, решение что искалось изначально: http://habrahabr.ru/post/241941/ :)

AlexGyver
Offline
Зарегистрирован: 12.03.2017

Привет! Удалось реализовать идею по этой статье с хабра? Можно взглянуть на ардуино-вариант для решения исходной задачи?