enum - выбор следующего шага?

Тема в разделе "Arduino & Shields", создана пользователем ИгорьК, 13 май 2014.

  1. ИгорьК

    ИгорьК Гуру

    Коллеги! Есть код:
    Код (C):
    enum unitMode {
        modeOn,
        modeOnMoode,
        modeDutyNormal,
        modeSecurity,
    } modesOfUnit;
    Задача: на некоторое событие выбрать следующий режим modesOfUnit.
    Пишу:
    Код (C):
    if (некоторое событие) {
        modesOfUnit  += (unitMode)1;
    }
    Ругается! Кто знает решение?
    Не проходит также modesOfUnit += 1; и modesOfUnit++;
     
    Последнее редактирование: 13 май 2014
  2. Mitrandir

    Mitrandir Гуру

    Попробуй в енуме пронумеровать позиции =1 = 2 поосле каждого элемента вписать
     
  3. Megakoteyka

    Megakoteyka Оракул Модератор

    Воспользуйтесь насильным приведением типа:
    Код (Text):
    modesOfUnit = (unitMode)(modesOfUnit + 1);
     
    ИгорьК нравится это.
  4. Megakoteyka

    Megakoteyka Оракул Модератор

    В енуме автоматически все нумеруется от 0. Нумеровать вручную нужно в тех случаях, когда надо присвоить свои определенные значения.
    Код (Text):
    enum En {
      A = 5,
      B,
      C = 8
    }
    B будет равно 6.
     
  5. ИгорьК

    ИгорьК Гуру

    modesOfUnit = (unitMode)(modesOfUnit + 1); - решено!
     
    Megakoteyka нравится это.
  6. Unixon

    Unixon Оракул Модератор

    Еще так можно:

    Код (Text):
    enum
    {
     ModeA,ModeB,ModeC,ModeD,ModesCount
    }
    ModesCount будет автоматически получать значение количества режимов перед ним, можно его использовть в циклах и сравнениях в качестве верхней границы.
     
    sendsay, Tomasina и Megakoteyka нравится это.
  7. Корней

    Корней Гик

    А потом присвоить, например, ModeC, значение, большее 2 и переписать половину кода. ;)
     
  8. Unixon

    Unixon Оракул Модератор

    Ну если ССЗБ - тогда пожалуйста.
     
  9. Корней

    Корней Гик

    Когда код в одном (хорошо, если в одном) месте, а enum (странно, почему он не называется, например, sequence) - в другом, а компилятору дали по башке, то от синдрома ССЗБ не застрахован никто.

    Но ни с кем из присутствующих такого конечно же никогда не произойдет.
     
  10. NR55RU

    NR55RU Гик

    Перечисление это по сути именованные константы, арифметические операции с ними по умолчанию не возможны (что защищает переменные перечислений от случайного изменения арифметикой) , хотя если перечисление используется в арифметической операции оно преобразуется к int но вот присвоить int переменной перечисления нельзя, ну если не использовать грубую силу явного приведения типов.
    И подобные операции существенно снижают читабельность кода ибо нарушается их прямое предназначение - именованность, более того при арифметических операциях есть риск выскочить за диапазон значений перечисления что повышает вероятность ошибок которые могут выскакивать не постоянно.

    ( Если кому интересно, диапазон значений перечисления определяются так, верхняя границы, берется самое большое значение перечисления и берется самое близкое к нему число кратное степени двойки которое больше этого крайнего значения, то есть если последняя константа имеет значение 100 то верхняя граница будет равно 128, нижняя граница определяется так, если минимальное значение 0 или больше нуля, то нижняя граница будет 0, если число отрицательное то по аналогии с верхней границей )

    Отсюда у меня вопрос, а существуют ли ситуации в которых арифметические операции с перечислениями действительно необходимы ? (просто у меня мало опыта в разработке крупных вещей на С/С++ а у кого то может есть и он опишет ситуацию в которой плюсы от таких действий перешивают все минусы)
     
  11. geher

    geher Гуру

    Арифметические операции с перечислениями - это некорректно с точки зрения логики.
    Если предполагается какая-то арифметика, то логичнее использовать набор констант, определенных при помощи #define.
    А перечисления создавались чтобы имелась возможность присваивать какой-то переменной некий абстрактный набор значений, ни с чем не связанный. Соответственно, оправданных операций для перечисления только шесть: ==, !=, предыдущий, следующий, первый и последний. Остальное просто не соответствует самой логике перечисления.
    Привязка к числовым значениям в данном случае не более чем "костыль".
     
    Tomasina, Megakoteyka и Mitrandir нравится это.
  12. ИгорьК

    ИгорьК Гуру

    Ок. Решение уже есть. но комментарии продолжаются и смысл их ясен.
    Исходя из моей задачи, я перечислил четыре режима работы программы.
    Режимы меняются последовательно по некоторому событию (нажимаем кнопку - следующий режим) и используются в switgh/case многократно (в зависимости от режима, проверяется или не проверяется тот или иной датчик).
    Как тогда это реализовать правильно? В принципе, можно обычной переменной без всяких "объяснений-перечислений". Или?

    Как это делается для арифметики?
     
    Последнее редактирование: 15 май 2014
  13. NR55RU

    NR55RU Гик

    Примерно так:
    Код (Text):

    const int modeOnMoode = 1;
    const int modeDutyNormal = 2;
    const int modeSecurity = 3;

    int modesOfUnit = modeOnMoode;
    modesOfUnit++;

    switch( modesOfUnit )
    {
       case modeOnMoode:
         // do something
         break;
       case modeDutyNormal:
         // do something
         break;
       case modeSecurity:
         // do something
         break;
    }
     
     
  14. ИгорьК

    ИгорьК Гуру

    Ну да. Но как-то искусственно. Те же константы, плюс переменная. Мне с насильным приведением типа больше нравится, и, ИМХО, более наглядно. А необходимость арифметики с перечислениями я описал и мне кажется это типовой случай.
    Код (Text):
    if (нажали кнопку) {
    modesOfUnit = (unitMode)(modesOfUnit + 1);
       if (modesOfUnit > максимального) {
          modesOfUnit = нулевому;}
    }
     
    Последнее редактирование: 15 май 2014
  15. geher

    geher Гуру

    Собственно говоря, разговор ушел в сторону "чистоты" языка.
    Конструкция
    modesOfUnit = (unitMode)(modesOfUnit + 1)
    формально с точки зрения языка верна и даже работоспособна, но вроде как считается финтом ушами, а эти самые финты не рекомендуются к употреблению в программировании, поскольку снижают читаемость и логичность кода.
    В частности, эта конструкция может замаскировать ошибку, при которой переменной перечислимого типа будет присвоено (сделана попытка присваивания?) некорректное значение.

    Возможно адепты другого стиля программирования со мной не согласятся, но формально правильней было бы либо избежать арифметики с enum, обявив функцию вроде следующей:
    Код (Text):
    unitMode nextMode (uintmode u)
    {
      switch (u){
        case modeOn:
          return(modeOnMoode);
          break;
        case modeOnMoode:
          return(modeDutyNormal);
          break;
        case modeDutyNormal:
           return(modeSecurity);
           break;
        default:return(modeOn); // возврат к первому режиму
    }
    либо использовать по старинке константы
    Код (Text):
    #define modeOn 0
    #define modeOnMoode 1
    #define modeDutyNormal 2
    #define modeSecurity 3

    int modesOfUnit=modeOn;
    В этом случае использование целочисленной переменной намекает читающему код на возможность использования произвольной арифметики со всеми возможными последствиями.

    Другой вопрос, что "правильный с точки зрения чистоты кода" вариант может оказаться недостаточно эффективным по затратам процессорного времени и памяти.
    И в случае программирования микроконтроллеров я считаю вполне допустимым использование "недостойных" трюков и даже "проклятой" команды goto, поскольку такие трюки позволяют оптимизировать программу, пусть даже путем потери "чистоты" кода.
    Но даже в этом случае, если внимать адептам "чистого программирования", настоятельно рекомендуется сначала написать программу "красиво", и только потом "увечить" ее оптимизацией, причем, насколько возможно, доверить этот процесс компилятору.
     
    Последнее редактирование: 15 май 2014
    ИгорьК нравится это.
  16. Mitrandir

    Mitrandir Гуру

    Предпочитаю дефайны константам
     
  17. Корней

    Корней Гик

    Действительно, зачем напрягать компилятор, когда можно все переложить на препроцессор. ;)
     
  18. ИгорьК

    ИгорьК Гуру

    Рискну показаться упертым... Но над Вашим (geher) первым примером пришлось даже напрягать голову. Мне кажется в предыдущем моем посте логика более понятна.
    Выкладываю свой пример. Есть два флага - короткое и длинное нажатие кнопки. Мне кажется, даже не нужны комментарии как это работает:
    Код (C):
    case stepWhatToDo:
                if (flag.bottonShort) {
                    modesOfUnit = (unitMode)(modesOfUnit + 1);
                    if (modesOfUnit >= modeSecurity) {// modeSecurity - максимальное значение
                        modesOfUnit = modeOn;         // и включается долгим нажатием кнопки,
                    }                                // а выключается как долгим так и коротким;
                    flag.bottonShort = 0;
                }
                if (flag.bottonLong) {
                    if (modesOfUnit == modeSecurity) {
                        modesOfUnit = modeDutyNormal;
                    }
                    else {
                        modesOfUnit = modeSecurity;
                    }
                    flag.bottonLong = 0;
                }
    break;
     
    Последнее редактирование: 16 май 2014
  19. Megakoteyka

    Megakoteyka Оракул Модератор

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

    Tomasina Сушитель лампочек Модератор

    а зачем так навороченно?
    Вот фрагменты из моего самодельного меню по ЖК-экрану:
    Код (Text):
    enum ScreenID
    {
      INFO_SUMMARY,
      INFO_TEMPERATURE,
      INFO_VOLTAGE,
      INFO_LUMI,
      MENU_SETTINGS,
      MENU_DATETIME,
      MENU_ABOUT,
      SETUP_SENSORS,
      COUNT_ITEMS
    }

    ...
    //////// Обработчик энкодера (вызывается из прерывания)
    if (encoderRight)          // если вращаем ручку вправо
    {
      timeout = millis();      // обнуляем таймаут
      screenChange = true;      // флаг, что экран надо перерисовать
      currentScreen++;          // переходим на следующий экран
      if (currentScreen == COUNT_ITEMS) currentScreen = INFO_SUMMARY; // по достижении крайнего пункта перескакиваем на начало
    }

    ...
    /////// обработчик экранов дисплея
    if (screenChange)
    {
      lcd.clear();
      screenChange = false;
      switch (currentScreen)
      {
      case INFO_SUMMARY:
          displaySummaryData();
          break;
      case INFO_TEMPERATURE:
          readSensors();
          displayTemperature();
          break;
      case INFO_VOLTAGE:
          readVoltage();
          displayVoltage();
          break;
    ...
      case MENU_SETTINGS:
          editSettings();
          break;
      }
    }
     
    Последнее редактирование: 15 май 2014
    ИгорьК нравится это.