Забавы с оператором switch

    Был у меня простой, рабочий код (я убрал лишнее оставил только суть):
    typedef enum
      {
        enNone,
        enOne,
        enTwo,
        enThree
      }TEnum;
    
        switch(Enum)
          {
            case enNone:
                 /*Ничего не делаем*/
                 break;/*enNone*/
            case enOne:
                 Value=f1(Value);
                 Value=A*Value+B;
                 break;/*enOne*/
            case enTwo:
                 Value=f2(Value);
                 Value=A*Value+B;
                 break;/*enTwo*/
            case enThree:
                 Value=f3(Value);
                 Value=A*Value+B;
                 break;/*enThree*/
          }/*SWITCH*/
    


    Больших претензий к нему у меня не было, но решил его микрооптимизировать:

    Решил устранить избыточность и вынести отдельно общее преобразование.
    Получилось так:
        if(enNone!=Enum)
          {
            switch(Enum)
              {
                case enOne:
                     Value=f1(Value);
                     break;/*enOne*/
                case enTwo:
                     Value=f2(Value);
                     break;/*enTwo*/
                case enThree:
                     Value=f3(Value);
                     break;/*enThree*/
              }/*SWITCH*/
            Value=A*Value+B;
          }/*THEN*/
    


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

    Я стараюсь программировать так, чтобы не было warning'ов, потому задумался, а что здесь можно сделать, и вспомнив устройство Даффа, сделал так:

        switch(Enum)
          {
            case enNone: break;
            do
              {
                case enOne:
                     Value=f1(Value);
                     break;/*enOne*/
                case enTwo:
                     Value=f2(Value);
                     break;/*enTwo*/
                case enThree:
                     Value=f3(Value);
                     break;/*enThree*/
              }while( 0 );
            Value=A*Value+B;
          }/*SWITCH*/
    


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

    Этот код работает только потому, что в языке Си для оператора switch ограничения на составной оператор ослаблены настолько, что требуется только чтобы он был правильно составлен и метки case: стояли перед операторами.
    Только первый оператор break; выходит из конструкции switch, остальные выходят из цикла do {} while.

    Ещё я знаю, что такую особенность switch используют для организации сопроцессов в Си, но, к сожалению, пример такого полезного применения этой особенности не удалось найти.

    Комментарии 9

      +1
      А default: — религия не позволяет?
        +1
        Позволяет, конечно, только в форме default: assert(0);
        так и было сделано, но было интересно попробовать по другому.
          0
          default: assert(0) в вашем случае будет ошибочным, потому что по enNone ничего делать не надо, ибо это не ошибка.

          достаточно просто default:

            0
            Так оно до assert(0) и не дойдёт никогда, поэтому ошибка никогда не сгенерируется, но да, писать так всё равно не нужно.
              0
              =) почему не нужно — нужно, если предполагается, что enNone невалидное значение.

              Судя же по первому варианту кода — enNone вполне валидное значение, но по нему ничего не должно происходить — вот и не надо assert(0) ставить.

                +1
                У меня в конечном итоге получился следующий вариант:
                    if(enNone!=Enum)
                      {
                        switch(Enum)
                          {
                            case enOne:
                                 Value=f1(Value);
                                 break;/*enOne*/
                            case enTwo:
                                 Value=f2(Value);
                                 break;/*enTwo*/
                            case enThree:
                                 Value=f3(Value);
                                 break;/*enThree*/
                            default:assert( 0 );
                                 break;
                          }/*SWITCH*/
                        Value=A*Value+B;
                      }/*THEN*/


                default конечно, никогда не выполнится, вплоть до последующего изменения типа, но тогда assert отловит эту ситуацию при тестах.
                Именно поэтому он там необходим.
                  0
                  Да не, я имел ввиду, что часть default в таком коде никогда не выполнится — выше цикл по условию "!=enNone", а остальные значения «отловятся» выше в ветках switch-case. Потому и не важно что там стоит. Но ниже, кстати, автор прав — если в будущем возможно расширение перечисления, то assert вроде как и к месту вполне, тут я соглашусь всё же.
          0
          Устройство Даффа — это метод оптимизации циклов. У Вас тут не цикл, а обычное ветвление, так что метка не совсем в тему.
            0
            В устройстве Даффа также используется особенность определения оператора switch, потому и упомянул.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое