Итак, вы хотите заглушить это предупреждение в Visual C++…

    FAILОбычная ситуация: вы написали кусок безупречно правильного кода, но Visual C++ выдает на нем предупреждение. Часто можно немного переписать код, чтобы предупреждение ушло, но не всегда, и тогда выход один – глушить выдачу этого предупреждения.

    Рассмотрим, какие возможности для этого есть в Visual C++ и какие ошибки допускают при их использовании.



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

    Следующая возможность – запретить предупреждение в настройках проекта на уровне файла. Этот способ еще хуже предыдущего. Во-первых, предупреждение будет заглушено на всю единицу трансляции, т.е. в этом файле и всех заголовках, которые он включает. Во-вторых, точно те же проблемы с копированием кода, что и в прошлый раз. В третьих, как только в проекте оказывается больше нескольких файлов, вероятность потерять эту настройку при конверсии проекта под более новую версию Visual C++ становится равной чуть менее, чем единице.

    Остается использование #pragma warning. Обычно ее используют так:

    #pragma warning (disable: 9000)
    // код, провоцирующий предупреждение C9000
    #pragma warning (default: 9000)
    

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

    На самом деле, это FAIL. Пора внимательно прочитать (да, внимательно и да, прочитать, а не копипастить код откуда попало) описание #pragma warning (default). Там говорится следующее: эта конструкция

    1. устанавливает предупреждению уровень по умолчанию и
    2. включает предупреждение.

    Сначала уровни. В Visual C++ с каждым предупреждением связано число от 1 до 4 – это уровень предупреждения. Предупреждения уровня 1 считаются более серьезными, с ростом уровня серьезность якобы снижается. У каждого предупреждения есть уровень по умолчанию. Конструкция

    #pragma warning(Level: Warning)
    

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

    У компилятора есть настройка, с какого уровня предупреждения показывать, Warning Level. При значении этой настройки, равном A, предупреждение в конкретной строке кода показывается только в том случае, если оно там разрешено и его уровень составляет A или ниже.

    Кроме того, в Visual C++ часть предупреждений по умолчанию выключена, потому что они выдаются даже в самом безобидном коде и все от них устали. Пусть каждый, кто собирается возмутиться самой идеей точечного подавления конкретного предупреждения, для начала осознает и прочувствует этот факт.

    Рассмотрим, как проявляется FAIL при использовании #pragma warning (default).

    FAIL 1. Предупреждение C9001 выключено по умолчанию. Код в заголовочном файле использует #pragma warning(default:9001), чтобы «восстановить» предупреждение, заглушенное в небольшом куске кода.

    Зачем он это делает, если предупреждение и так выключено? Список предупреждений, выключенных по умолчанию, меняется от одной версии Visual C++ к другой – в него понемногу добавляются предупреждения. Если код изначально писали для Visual C++ 7, а там C9001 по умолчанию было включено, а теперь компилируют в Visual C++ 10, и в нем предупреждение уже выключено, то такая конструкция смысла не имеет, а могла просто достаться в наследство.

    В результате #pragma warning(default) принудительно включает предупреждение, выключенное по умолчанию.

    FAIL 2. У предупреждения C9002 уровень по умолчанию 3, а проект компилируется с уровнем 2, т.е. «показывать предупреждения уровня 2 и ниже». После долгих раздумий разработчики решили, что на самом деле предупреждение C9002 достаточно серьезное, чтобы удостоить его уровня 2, т.е. принудительно повысить серьезность. Соответственно, каждый проект включает в себя стандартный заголовок, попадающий затем во все единицы трансляции, который содержит конструкцию #pragma warning(2:9002).

    Чуть ниже по тексту в единице трансляции оказывается #pragma warning(default:9002), которая сбрасывает уровень обратно на 3, и при компиляции с уровнем 2 предупреждение не выдается. Кстати, это предупреждение сообщало о серьезном дефекте. Улыбаемся и машем. В обратную сторону тоже работает — предупреждению «повысили» уровень с 2 до 3, чтобы оно не выдавалось в проектах, компилируемых с уровнями 2 и ниже (т.е. понизили серьезность), но #pragma warning(default) сбрасывает уровень на 2, и предупреждение выдается.

    FAIL 3. Предупреждение C9003 по умолчанию включено, но продумано так плохо, что никто не может припомнить, когда оно выдается по делу. Разработчики решаются заглушить его повсюду, использовав #pragma warning(disable:9003) в общем заголовочном файле. Ниже по единице трансляции оказывается #pragma warning(default:9003), которая включает предупреждение.

    Что особенно приятно, эти ситуации возникают в самый подходящий момент – при переходе с одной версии компилятора на другую, при попытке включить в проект кучу стороннего кода, который и без того не прост в использовании, вторая ситуация просто приводит к подавлению предупреждения, в результате можно пропустить ошибку в коде, которая позже поможет пользователям получить премию Дарвина.

    На самом деле, предупреждения нужно глушить так:

    #pragma warning(push)
    // хорошие, годные вакансии: www.abbyy.ru/vacancy
    #pragma warning(disable:9000)
    // код с предупреждением C9000
    #pragma warning(pop)
    

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

    Выглядит страшно, но чего не сделаешь, чтобы не пропустить ту самую ошибку на миллиард.

    Дмитрий Мещеряков,

    департамент продуктов для разработчиков
    ABBYY
    136.51
    Решения для интеллектуальной обработки информации
    Share post

    Comments 27

      +6
      Ай-ай-ай, реклама в коде.
      К чести M$ можно сказать, что в VS хотя бы можно узнать код предупреждения, чтобы его отключить. А в gcc эти коды запрятаны неведомо куда. Поэтому лучше переписывать каждый раз код, чтобы компилятор не смог ни к чему придраться.
        +1
        Переписать код так, чтобы компилятор не смог ни к чему придраться не всегда возможно, особенно если в проекте поставлены параноидальные настройки. Тоже можно сделать и в XCode:

        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        // код с предупреждением
        #pragma clang diagnostic pop


        clang / GCC — в зависимости от используемого компилятора.
        А сам код ("-Warc-performSelector-leaks" например) можно узнать в Log Navigator.
          +2
          Я обычно пишу код с самыми параноидальными настройками (за исключением разве что -Wunreachable-code) — уменьшать уровень параноидальности приходится в основном из-за boost/qt и прочих сторонних библиотек, чьи заголовки с параноидальными настройками сразу ломаются.
            +1
            «Тоже можно сделать и в XCode»
            Это пошло из gcc (способ работает в том числе и на линуксе), XCode тут непричем.
              +1
              В XCode сам код можно узнать в Log Navigator. Это ответ на то что «в gcc эти коды запрятаны неведомо куда». В других распространенных IDE узнать эти коды не намного сложнее
          –3
          Разрешите взоразить. Предупреждения, они обычно неспроста. А значит, код не идеален. А раз код не идеален, значит нужно его исправить, а не заглушить предупреждение. И да, это относится даже к самым параноидальным уровням настройкам.

          … И кстати да, ИМХО глушение предупреждений — просто костыль.
            +1
            p.s. Если я не вернусь, прошу считать меня Code Nazi.
              +1
              Интересная мысль. В Visual C++ 10 по умолчанию выключены несколько десятков предупреждений. Как вы думаете, почему?
                0
                Потому, что некоторые из них необязательны, и ЕМНИП есть даже взаимоисключающие. Вы выбираете такой набор, который подходит вашему стилю разработки, и строго придерживаетесь его. А делать в одном месте так, а в другом — воттак — ИМО плохой стиль, со всеми вытекающими.
                  0
                  Это просто прекрасно. С одной стороны — предупреждения неспроста, с другой — необязательны. И кто-то еще должен раз и навсегда решить, какие из них важные, а какие нет.
                    0
                    Те, которые необязательны и заглушены, в большинстве своем относятся к стилю. И согласитесь, что по стилю решения должны приниматся в первых кем-то, а во вторых — раз и навсегда.
                      0
                      >Те, которые необязательны и заглушены, в большинстве своем относятся к стилю.

                      Меня интересуют конкретные примеры предупреждений в Visual C++, которые «относятся к стилю».
                0
                Не могу сказать ничего о предупреждениях в MSVS, но в гцц есть, например:
                • -Wunreachable-code, который довольно часто выдаётся в библиотеках;
                • -Wreorder, который, как правило, роли не играет;
                  -Wold-style-cast, от которого тоже никакого профита — кому, кроме pure C++-наци, какая разница, каким способом я скастую int в long, сишный способ просто короче;

                  И эти совершенно незначительные предупреждения вместе с -Werror сломают компиляцию.
                  0
                  Ох, чёрт, потерял закрывающий </b> после «как правило».
                    +3
                    >-Wold-style-cast, от которого тоже никакого профита — кому, кроме pure C++-наци, какая разница, каким способом я скастую int в long, сишный способ просто короче;

                    int->long действительно все равно как приводить, а в случае указателей есть разница.
                      0
                      Есть, не спорю. Но ворнинг-то выдастся в любом случае)
                        +2
                        А это уже потому, что предупреждение плохо продумано.
                      0
                      Да нет никакой разницы, какие именно предупреждения вы для себя решили считать важными, а какие — нет. Единственное, что существенно — это то, что те, которые решили считать важными, считаются важными повсюду. Тоесть — по всему проекту. И если мы решаем использовать сишный каст — пожалуйста, только давайте будем использовать его всюду, а не как Бог на душу положит… В особенности обрамляя те места, где захотелось, дополнительными pragmaми.

                      Именно поэтому я считаю, что
                      1) настройки предупреждений должны быть на уровне проекта, а не размазаны по исходникам.
                      2) задавливание предупреждения в коде должно быть настолько исключительным, что на него должно получать визу у техлида, или что-то в этом роде, и сопровождаться детальным комментом, зачем и почему, и для чего, и почему нельзя это обойти. За мои семь с хвостиком лет работы я видел аж один такой случай, и я до сих пор сомневаюсь, нельзя ли было там сделать лучше.

                      Зачем такая жестокость? Чтоб в один прекрасный день не найти задавленные каким-нибудь джуниором присвоение в if-е, чисто чтоб компилилось.

                      p.s. аргумент в статье о копировании кода считаю смешным. DRY.
                        0
                        Пост не о том, нужно ли давить предупреждения и можно ли давить предупреждения, он о том, как их давить после получения той самой визы у техлида.

                        DRY — отлично, но каждый раз как в коде встречается #include, код из заголовка копируется в текущую единицу трансляции. Кстати, в тексте поста об этом тоже говорится.
                          0
                          Да, я знаю. Но простите, я не могу представить себе ситуацию, когда нужно задавливание ворнинга в хедере, поэтому принял этот аргумент на счет копирования исполняемого кода.
                            0
                            Реализация шаблонов обычно как раз в заголовках. Иногда может возникнуть идея задавить там предупреждение.
                    0
                    А еще в VS есть конструкция warning:suppress — проигнорировать определенный ворнинг на ближайшей строке.

                    Кроме того, вместо #pragma можно писать __pragma() — в таком виде его можно использовать в макросах:
                    #define __const_cond( c ) \
                        __pragma(warning(push)) \
                        __pragma(warning(disable:4127)) \
                        ( c ) \
                        __pragma(warning(pop))
                      0
                      Если верить MSDN, warning:suppress работает только для предупреждений с кодом 6000 и выше.
                        0
                        Cсылка ведет на доку для VS2005. В более поздних такого ограничения нет (пруфлинк).
                        0
                        Крутняк, спасибо!
                          +1
                          Мне очень нравится как в исходниках Chromium подобным образом борются с варнингом Compiler warning C4355: 'this': used in base member initializer list:

                          #define ALLOW_THIS_IN_INITIALIZER_LIST(code) MSVC_PUSH_DISABLE_WARNING(4355) \
                                                                       code \
                                                                       MSVC_POP_WARNING()
                          
                          Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {}
                          0
                          Прибыль?

                          Only users with full accounts can post comments. Log in, please.