Как избавиться от предупреждения если в case нет break?

Модератор: Модераторы разделов

MiK13
Сообщения: 841
ОС: Linux Debian

Как избавиться от предупреждения если в case нет break?

Сообщение MiK13 »

Если в операторе switch в каком-то из case нет break;, то если при компиляции задана опция -Wextra, то компилятор выдаёт предупреждение типа

Код: Выделить всё

file.c:1939:9: warning: this statement may fall through [-Wimplicit-fallthrough=]
В принципе предупреждение полезное, так как можно просто забыть вставить break;
Но если он не нужен? То есть два значения переключателя требуют одного действия, но одно из них требует ещё чего-то предварительного?
Как быть в этом случае?
Через Яндекс я нашёл такую ссылку
Правда дам почему-то говорится об ошибке а не предупреждении. Но у меня вопрос не по причине.
В качестве ответа написано:
У вас нет break; в конце вашего case: так что выполнение провалится в следующий case. Добавьте оператор break чтобы предотвратить [[fallthrough]] если это то, что вы хотите, или добавить атрибут [[fallthrough]] если подразумевается провал.
И меня интересует: как (куда) добавить атрибут [[fallthrough]]?
Спасибо сказали:

MiK13
Сообщения: 841
ОС: Linux Debian

Re: Как избавиться от предупреждения если в case нет break?

Сообщение MiK13 »

В принципе проблему можно считать решённой,
В этой статье нашёл пример

Код: Выделить всё

switch (i)
{
case 10:
  f1();
  break;
case 20:
  f2();
  [[fallthrough]]; // Предупреждение будет подавлено
case 30:
  f3();
  break;
case 40:
  f4();
  break;
}
Но компилятор [[fallthrough]]; не понял. Сказал, что это ошибка.
Но в man gcc нашёл такой пример

Код: Выделить всё

switch (cond)
{
  case 1:
    bar (0);
    __attribute__ ((fallthrough));
  default:
    ...
}
Поставил __attribute__ ((fallthrough)); вместо [[fallthrough]]; и предупреждение исчезло.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

MiK13 писал:
21.01.2020 01:48
Но компилятор [[fallthrough]]; не понял. Сказал, что это ошибка.
Это появилось только в C++17.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

MiK13
Сообщения: 841
ОС: Linux Debian

Re: Как избавиться от предупреждения если в case нет break?

Сообщение MiK13 »

Bizdelnick писал:
21.01.2020 10:20
MiK13 писал:
21.01.2020 01:48
Но компилятор [[fallthrough]]; не понял. Сказал, что это ошибка.
Это появилось только в C++17.
Понятно. Но только в C++? В простом C этого нет?
MiK13 писал:
21.01.2020 01:48
Поставил __attribute__ ((fallthrough)); вместо [[fallthrough]]; и предупреждение исчезло.
Но ненадолго.
Дома исчезло (компилятор 8-й версии, в Buster).
А вот на работе возникло другое:

Shell

progrd.c: In function ‘main’:
progrd.c:1940:9: warning: empty declaration
__attribute__ ((fallthrough));
^~~~~~~~~~~~~
Видимо, gcc более ранней версии это не понимает.
Поэтому возник другой вопрос.
Можно ли как-то директивами условной компиляции ограничить этот атрибут?
То есть чтобы эта строка компилировалась только если gcc версии 8 и старше.
Спасибо сказали:

Аватара пользователя
/dev/random
Администратор
Сообщения: 5018
ОС: Gentoo

Re: Как избавиться от предупреждения если в case нет break?

Сообщение /dev/random »

MiK13 писал:
21.01.2020 12:11
Видимо, gcc более ранней версии это не понимает.
Можно ли как-то директивами условной компиляции ограничить этот атрибут?
Попробуйте вместо этой строки использовать комментарий, состоящий из одного слова: "FALLTHROUGH" (без кавычек). Вроде бы, в старых версиях так было.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

Вот тут подробно этот вопрос разобран.
Ну и см. документацию.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2883
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Hephaestus »

MiK13 писал:
21.01.2020 01:29
То есть два значения переключателя требуют одного действия, но одно из них требует ещё чего-то предварительного?
Как быть в этом случае?
Простите, что вмешиваюсь, но...
Если в текущем блоке case не указана инструкция break , и мы тем самым "проваливаемся" в следующий блок case,
то с точки зрения выполняемых инструкций получается, что это один блок case, а не два. Причем, это свойство языка, а не компилятора.
Так может быть, в этом случае блок case и должен быть один?
Может быть, стоит по-другому организовать switch?
На мой взгляд, это более рационально, чем воевать с компилятором, да ещё отдельно с разными версиями.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

MiK13
Сообщения: 841
ОС: Linux Debian

Re: Как избавиться от предупреждения если в case нет break?

Сообщение MiK13 »

Hephaestus писал:
21.01.2020 13:35
MiK13 писал:
21.01.2020 01:29
То есть два значения переключателя требуют одного действия, но одно из них требует ещё чего-то предварительного?
Как быть в этом случае?
Простите, что вмешиваюсь, но...
...
Так может быть, в этом случае блок case и должен быть один?
Может быть, стоит по-другому организовать switch?
В принципе можно. Но это, скорее всего приведёт к дублированию вызовов.
У меня при обработке запроса надо добавить в некоторый массив группу данных. Или просто занести.
Второе действие представляет собой первое, только с предварительной очисткой массива. Поэтому я и решил сделать:

Код: Выделить всё

case req2: Clear();
case req1: Store(); break;
Hephaestus писал:
21.01.2020 13:35
На мой взгляд, это более рационально, чем воевать с компилятором, да ещё отдельно с разными версиями.
Войны с компилятором нет.
Просто проблема возникла из-за того, что стали переходить на 64-битные версии. И установили новые системы с новыми компиляторами.
Но надо сопровождать и 32-битные. К тому же на старых ядрах.
Уже два раза сталкивался с проблемами.
Приехал с нетбуком, на котором Debian 9, а там Debian 6. Старые библиотеки. Помогла опция -static.
Была и более серьёзная проблема -- программа при запуске выдала, что старое ядро (там, по-моему, вообще был Debian 4). К счастью у заказчика был ноутбук с этой версией системы.
Спасибо сказали:

IMB
Сообщения: 2471
ОС: Debian

Re: Как избавиться от предупреждения если в case нет break?

Сообщение IMB »

Код: Выделить всё

switch (param) {
case 1:
    .........
    break;
case 2:
case 3:
    if (param == 3) {
        .........
    } else {
        .........
    }
    break;
...........
}
если такие случае единичные
Спасибо сказали:

Аватара пользователя
s.xbatob
Сообщения: 921
ОС: Fedora

Re: Как избавиться от предупреждения если в case нет break?

Сообщение s.xbatob »

MiK13 писал:
21.01.2020 16:11
Просто проблема возникла из-за того, что стали переходить на 64-битные версии. И установили новые системы с новыми компиляторами.
Но надо сопровождать и 32-битные. К тому же на старых ядрах.
Распустили вы их! :) Пусть обновляются.
Хотя, у нас зоопарк у клиентов тоже был (я так понимаю, что и сейчас есть, но я там уже не работаю). Решал я его с помощью локального репозитория. Собирать, правда, приходилось для каждой платформы в своей виртуальной машине.
А в этом конкретном случае надо написать макрос, который раскрывается по-разному в зависимости от используемой версии компилятора.
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2883
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Hephaestus »

MiK13 писал:
21.01.2020 16:11
Но это, скорее всего приведёт к дублированию вызовов.
Не обязательно.
MiK13 писал:
21.01.2020 16:11
У меня при обработке запроса надо добавить в некоторый массив группу данных. Или просто занести.
Второе действие представляет собой первое, только с предварительной очисткой массива.
Ну что ж, позвольте изложить чуть подробнее.
Книг по языку Си я видел не очень много, но штук пять-шесть навскидку могу припомнить.
И практически везде наблюдалась одна и та же картина: оператор switch вводится после if...else
примерно в таком контексте: вот у нас есть несколько условий (штук десять), if...else (который мы уже знаем) использовать неудобно, слишком громоздкая конструкция получается, поэтому вот вам switch.
То есть switch и if...else делают примерно одно и то же.
И если это так, то отсюда следуют две вещи:
1. Условия в switch взаимоисключающие.
2. В случае небольшого количества условий можно обойтись if...else.

В Вашем случае условия в switch взаимоисключающими не являются, насколько я понял.
Это уже повод пересмотреть код. Ну и по количеству условий - оно мне неизвестно, но может оказаться, что switсh вообще не нужен.

Можно либо иначе сформулировать условия, сделав их взаимоисключающими,
либо вообще не использовать switch.
Если Clear() вызывается только как предварительное действие к Store(),
но никогда не вызывается сама по себе, то отдельная ветка в switch ей нафиг не нужна - много чести.
В этом случае я бы её вызывал где-нибудь внутри Store() в нужный момент (при нужных условиях).

MiK13 писал:
21.01.2020 16:11
Войны с компилятором нет.
Ну, хорошо, не война. Возня.
Сборка прошла с предупреждением, что-то подшаманили, предупреждение исчезло,
пришли на другую машину/другой дистр/другую разрядность/другую версию - предупреждение опять появилось,
опять нужно шаманить, только уже иначе ну и т.д.
А всего-то и надо было, что отрефакторить несколько строк кода с учётом особенностей языка.

Я сам люблю, когда сборка происходит "чисто" - без предупреждений. И стараюсь этого добиться.
А в некоторых проектах, которые я видел, вообще всё сурово: предупреждения приравнены к ошибкам.
Были такие давние времена, когда в случае ошибок сборки исправляли компилятор, а не программу.
И эти интересные люди вроде бы даже ещё живы.
А мы с Вами сегодня можем позволить себе роскошь доверять компилятору.
И если уж мы обращаем внимание на его сообщения, то не зря же мы это делаем.
Мы же хотим наш код улучшить, а не просто "подавляющих атрибутов" напихать.
Таково моё ИМХО.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
s.xbatob
Сообщения: 921
ОС: Fedora

Re: Как избавиться от предупреждения если в case нет break?

Сообщение s.xbatob »

Hephaestus писал:
21.01.2020 22:04
2. В случае небольшого количества условий можно обойтись if...else.
Ну-ка, расскажи, как можно в реализации через if-else реализовать провал случаев? ;)
А вообще case очень удобен для реализации машины состояний, которая в свою очередь полезна в event-driven model, которое опять же используют все интерактивные программы. Особенно когда нужно реагировать побыстрее.
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

Hephaestus писал:
21.01.2020 22:04
Мы же хотим наш код улучшить, а не просто "подавляющих атрибутов" напихать.
Комментарий или атрибут, явно указывающий (программисту, а вовсе не только компилятору) на то, что происходит «провал» в следующее условие, и что это сделано осознанно, именно улучшает читаемость кода.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2883
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Hephaestus »

s.xbatob писал:
21.01.2020 22:23
Ну-ка, расскажи
Если Вам не трудно, давайте останемся на "вы". Спасибо.
s.xbatob писал:
21.01.2020 22:23
как можно в реализации через if-else реализовать провал случаев?
А зачем? Зачем нужно именно "провал случаев"?
Не будет "случаев" - не будет и "провалов". Можно же организовать по-другому.

В коде, приведенном выше

Код: Выделить всё

case req2: Clear();
case req1: Store(); break;
подразумевается вызов Store(), перед которым иногда вызывается Clear().

Здесь всего два условия и реализовать такую логику на if-else не составляет никаких проблем.
Будет что-то вроде

Код: Выделить всё

if (condition1){
    if (condition2)
        Clear();
    Store();
}
Эта конструкция выглядит не так красиво, как switch,
но она работает независимо от версии компилятора/разрядности и не требует всяких "специальных атрибутов".

Если бы "провалы случаев" были общепринятой практикой, то:
- это было бы отражено в литературе наряду с инструкцией switch;
- на это не было бы ругани компилятора;
- не пришлось бы искать [[fallthrough]].
А пока "провалы случаев" выглядят просто как побочный эффект в ситуации, когда break забыли поставить.
Bizdelnick писал:
21.01.2020 23:33
улучшает читаемость кода
Когда я говорил про "улучшить код", я имел в виду не столько читаемость, сколько стабильность (в том числе стабильность сборки), переносимость и т.п.
А то, понимаешь ли, воткнули атрибут - вроде собралось. Перенесли на другую машину - а там надо другой атрибут. Как-то это не совсем правильно, согласитесь.
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

Hephaestus писал:
22.01.2020 08:08
Зачем нужно именно "провал случаев"?
Иногда бывает удобно. Редко. Чтобы не дублировать один и тот же код в разных кейсах.
Hephaestus писал:
22.01.2020 08:08
Когда я говорил про "улучшить код", я имел в виду не столько читаемость, сколько стабильность (в том числе стабильность сборки), переносимость и т.п.
Любой соответсвующий стандарту компилятор такой код соберёт.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

Hephaestus писал:
22.01.2020 08:08
В коде, приведенном выше

Код: Выделить всё

case req2: Clear();
case req1: Store(); break;
подразумевается вызов Store(), перед которым иногда вызывается Clear().

Здесь всего два условия и реализовать такую логику на if-else не составляет никаких проблем.
Будет что-то вроде

Код: Выделить всё

if (condition1){
    if (condition2)
        Clear();
    Store();
}
Ошибочка, это другая логика.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
Hephaestus
Сообщения: 2883
Статус: Многоуважаемый джинн...
ОС: Slackware64-14.1/14.2

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Hephaestus »

Bizdelnick писал:
22.01.2020 11:33
Любой соответсвующий стандарту компилятор такой код соберёт.
Соберет-то он соберет.
Только вот ситуация получилась какая-то парадоксальная: ТС указал компилятору выводить максимум предупреждений,
а когда предупреждение таки вылезло, решил его подавить, не исправляя кода. Зачем, спрашивается, выводил тогда?
Ну, хорошо, вывел, проверил, что-то поправил, что-то решил оставить как есть...
Ну так можно при финальной сборке снизить уровень предупреждений. Тогда и fallthrough не понадобится.
А для тех, кто код будет читать, лучше оставить нормальный комментарий на человеческом языке.
Bizdelnick писал:
22.01.2020 11:35
Ошибочка, это другая логика.
Да, немного другая.
В исходном примере вызов Store(), получается, зависит от двух условий.
Тогда более точно было бы, наверно, как-нибудь так:

Код: Выделить всё

if (condition1 || condition2){
    if (condition2)
        Clear();
    Store();
}
Пускай скрипят мои конечности.
Я - повелитель бесконечности...
Мой блог
Спасибо сказали:

Аватара пользователя
Bizdelnick
Модератор
Сообщения: 16533
Статус: grammatikführer
ОС: Debian GNU/Linux

Re: Как избавиться от предупреждения если в case нет break?

Сообщение Bizdelnick »

Hephaestus писал:
22.01.2020 13:22
Только вот ситуация получилась какая-то парадоксальная: ТС указал компилятору выводить максимум предупреждений,
а когда предупреждение таки вылезло, решил его подавить, не исправляя кода. Зачем, спрашивается, выводил тогда?
Совершенно нормальная ситуация. Для этого ещё и отдельно статические анализаторы используют. Они выкидывают кучу фолсов, но вместе с ними и что-то полезное периодически. Давить фолсы, не меняя логики кода, в порядке вещей.
Hephaestus писал:
22.01.2020 13:22
А для тех, кто код будет читать, лучше оставить нормальный комментарий на человеческом языке.
Так там без вариантов, это же C, а не C++.
Hephaestus писал:
22.01.2020 13:22
Тогда более точно было бы, наверно, как-нибудь так
Да. Но тут выполняется целых три проверки вместо одной. Хотя компилятор, наверное, осилит это соптимизировать… Вот только зачем? Понять такой код не проще.
Пишите правильно:
в консоли
вкупе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:

Аватара пользователя
s.xbatob
Сообщения: 921
ОС: Fedora

Re: Как избавиться от предупреждения если в case нет break?

Сообщение s.xbatob »

Hephaestus писал:
22.01.2020 08:08
Если Вам не трудно, давайте останемся на "вы". Спасибо.
Да, на здоровье. Правда, этих краях такое воспринимается неоднозначно :)

В любом случае, вы предлагаете куда более сомнительные решения. Уж лучше посмотреть предупреждение. А как обойти по возможности я уже рассказал. Сам пользовался для override и final
Спасибо сказали:

MiK13
Сообщения: 841
ОС: Linux Debian

Re: Как избавиться от предупреждения если в case нет break?

Сообщение MiK13 »

Я решил сделать немного по-другому.
По-скольку наличие или отсутствие предупреждения зависит от версии компилятора (и от того, где буду транслировать), сделал так:

Код: Выделить всё

        #ifdef _TS_FALL
        __attribute__ ((fallthrough));
        #endif
В makefile для нового компилятора добавлю опцию -D _TS_FALL

Попутно возник ещё один вопрос про подавление предупреждений.
В программе на C++ (точнее, FLTK) есть код:

Код: Выделить всё

  Fl_Input val_aw(50, 5, 45, 22, _("∡ &Az:±"));
     ...
  val_aw.callback(set_aw, (void*)&val_aw);
Код самой функции:

Код: Выделить всё

void set_aw(Fl_Widget *w, void *data) {
    Fl_Input *in = (Fl_Input*)data;
    db->aw = atof(in->value());
}
Так, как это CallBack функция, то ей нужны два параметра. Первый параметр я никак не использую. И об этом предупреждает компилятор.
Поэтому я решил попробовать убрать имя параметра. То есть оставить void set_aw(Fl_Widget *, void *data)
Предупреждение пропало.
И поэтому один вопрос: это корректно?
Спасибо сказали: