GCC 7, -прозрачные предупреждения и переносимый способ их очистки?
Мы улавливаем предупреждения от GCC 7 для неявного падения в инструкции switch. Раньше мы очищали их в соответствии с Кланом (что причина для комментария, приведенного ниже):
g++ -DNDEBUG -g2 -O3 -std=c++17 -Wall -Wextra -fPIC -c authenc.cpp
asn.cpp: In member function ‘void EncodedObjectFilter::Put(const byte*, size_t)’:
asn.cpp:359:18: warning: this statement may fall through [-Wimplicit-fallthrough=]
m_state = BODY; // fall through
^
asn.cpp:361:3: note: here
case BODY:
^~~~
Руководство GCC позволяет использовать __attribute__ ((fallthrough))
, но не переносится. В руководстве также говорится: "... также можно добавить пропущенный комментарий, чтобы отключить предупреждение", но он предлагает только FALLTHRU
(это действительно единственный выбор?):
switch (cond)
{
case 1:
bar (0);
/* FALLTHRU */
default:
…
}
Есть ли переносимый способ очистить падение через предупреждение для Clang и GCC? Если да, то что это?
Ответы
Ответ 1
GCC ожидает комментарий маркера на своей собственной строке, например так:
m_state = BODY;
// fall through
case BODY:
Маркер также должен находиться прямо перед этикеткой case
; не может быть закрывающей фигурной скобки }
.
fall through
является одним из маркеров, признанных GCC. Это не просто FALLTHRU
. Полный список см. В документации по -Wimplicit-fallthrough
. Также смотрите эту запись в блоге Red Hat Developer.
(Это также должно быть совместимо с Clang, хотя я не могу заставить текущий транк (r308163) выдать предупреждение о переключении при провале, чтобы проверить это.)
Обратите внимание, что подавление предупреждения с помощью комментариев маркера работает только в том случае, если компилятор действительно видит комментарий. Если препроцессор работает отдельно, он должен быть проинструктирован для сохранения комментариев, как с -C
GCC). Например, чтобы избежать ложных предупреждений с кэш компилятора, необходимо указать -C
флаг при компиляции, или, с последними версиями кэш компилятора, используйте keep_comments_cpp
вариант.
Ответ 2
С++ 17 [[fallthrough]]
Пример:
int main(int argc, char **argv) {
switch (argc) {
case 0:
argc = 1;
[[fallthrough]];
case 1:
argc = 2;
};
}
Компилировать с:
g++ -std=c++17 -Wimplicit-fallthrough main.cpp
Если вы удалите [[fallthrough]];
, GCC предупредит:
main.cpp: In function ‘int main():
main.cpp:5:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
argc = 1;
~~^~~
main.cpp:6:9: note: here
case 1:
^~~~
Также обратите внимание на пример, что предупреждение появляется только в том случае, если вы попадаете в два случая: последний оператор case (здесь case 1
) не генерирует предупреждений, даже если он не имеет break
.
Следующие конструкции также не генерируют предупреждение:
#include <cstdlib>
[[noreturn]] void my_noreturn_func() {
exit(1);
}
int main(int argc, char **argv) {
// Erm, an actual break
switch (argc) {
case 0:
argc = 1;
break;
case 1:
argc = 2;
}
// Return also works.
switch (argc) {
case 0:
argc = 1;
return 0;
case 1:
argc = 2;
}
// noreturn functions are also work.
// https://stackoverflow.com/questions/10538291/what-is-the-point-of-noreturn/47444782#47444782
switch (argc) {
case 0:
argc = 1;
my_noreturn_func();
case 1:
argc = 2;
}
// Empty case synonyms are fine.
switch (argc) {
case 0:
case 1:
argc = 2;
}
// Magic comment mentioned at:
// https://stackoverflow.com/a/45137452/895245
switch (argc) {
case 0:
argc = 1;
// fall through
case 1:
argc = 2;
}
switch (argc) {
// GCC extension for pre C++17.
case 0:
argc = 1;
__attribute__ ((fallthrough));
case 1:
argc = 2;
}
switch (argc) {
// GCC examines all braches.
case 0:
if (argv[0][0] == 'm') {
[[fallthrough]];
} else {
return 0;
}
case 1:
argc = 2;
}
}
Из последнего видно, что GCC проверяет все возможные ветки и предупреждает, если у какого-либо из них нет [[fallthrough]];
или break
или return
.
Возможно, вы также захотите проверить наличие функций с помощью макросов, как в этом вдохновленном GEM5 фрагменте:
#if defined __has_cpp_attribute
#if __has_cpp_attribute(fallthrough)
#define MY_FALLTHROUGH [[fallthrough]]
#else
#define MY_FALLTHROUGH
#endif
#else
#define MY_FALLTHROUGH
#endif
Смотрите также: https://en.cppreference.com/w/cpp/language/attributes/fallthrough
Протестировано на GCC 7.4.0, Ubuntu 18.04.
Смотрите также
C версия этого вопроса: GCC 7, -Wimplicit-fallthrough предупреждений, и портативный способ их устранения?
Ответ 3
Как уже упоминалось, С++ 17 поддерживает атрибут [[fallthrough]]
. Только g++
имеет подвох (по крайней мере, версия 7.3.0). Если вы используете -Wextra
в командной строке и пропускаете break;
выражение, даже если вы [[fallthrough]]
, вы все равно получите предупреждение. Это потому, что он проверяет другие версии атрибута, а не С++ 17.
Если вы явно установите предупреждение, это решит проблему. Так что если у вас есть:
... -std=c++17 -Wextra ...
Это не удается.
Если у вас есть:
... -std=c++17 -Wextra -Wimplicit-fallthrough ...
Он работает только с атрибутом С++ 17. Я не пытался, но я предполагаю, что порядок может быть важным. При этом реализация -Wextra
может быть достаточно умной, чтобы не изменять предупреждение, которое уже настроено.
case 1:
blah = 5;
[[fallthrough]]; // works only if "... -Wimplicit-fallthrough ..." is used
Старый атрибут был __attribute__((fallthrough))
. g++ также может распознавать некоторые комментарии.
Ответ 4
Чистый раствор C:
int r(int a) {
switch(a) {
case 0:
a += 3;
case 1:
a += 2;
default:
a += a;
}
return a;
}
становится:
int h(int a) {
switch(a) {
case 0:
a += 3;
goto one;
one:
case 1:
a += 2;
goto others;
others:
default:
a += a;
}
return a;
}