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