Обычная ситуация: вы написали кусок безупречно правильного кода, но Visual C++ выдает на нем предупреждение. Часто можно немного переписать код, чтобы предупреждение ушло, но не всегда, и тогда выход один - глушить выдачу этого предупреждения. Рассмотрим, какие возможности для этого есть в Visual C++ и какие ошибки допускают при их использовании.
Статью написал сотрудник компании ABBYY Дмитрий Мещеряков, впервые опубликована: "Блог компании ABBYY. Итак, вы хотите заглушить это предупреждение в Visual C++..." Публикуется здесь с разрешения правообладателя.
Мы решили опубликовать эту статью в базе знаний, так как она очень хорошо поясняет назначение диагностики V665, реализованную в PVS-Studio.
Самая очевидная первая возможность - запретить предупреждение в настройках проекта на уровне проекта. Это работает, но плохо. Во-первых, предупреждение будет заглушено во всем проекте, в том числе и во всех заголовках, которые этот проект включает в себя. Во-вторых, если вы скопируете код в другой проект, предупреждение появится снова. Это неизбежно произойдет в случае кода в заголовочных файлах (например, содержащими реализацию шаблонов), который необходимо включать (#include) в каждый проект, который их использует.
Следующая возможность - запретить предупреждение в настройках проекта на уровне файла. Этот способ еще хуже предыдущего. Во-первых, предупреждение будет заглушено на всю единицу трансляции, т.е. в этом файле и всех заголовках, которые он включает. Во-вторых, точно те же проблемы с копированием кода, что и в прошлый раз. В-третьих, как только в проекте оказывается больше нескольких файлов, вероятность потерять эту настройку при конверсии проекта под более новую версию Visual C++ становится равной чуть менее, чем единице.
Остается использование #pragma warning. Обычно ее используют так:
#pragma warning (disable: 9000)
// код, провоцирующий предупреждение C9000
#pragma warning (default: 9000)
... и остаются очень довольны собой. Предупреждение заглушили, код написали, предупреждение восстановили. Прибыль.
На самом деле, это FAIL. Пора внимательно прочитать (да, внимательно и да, прочитать, а не копипастить код откуда попало) описание #pragma warning (default). Там говорится следующее: эта конструкция
Сначала уровни. В 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)
Первая конструкция сохраняет текущее состояние настроек предупреждений, вторая отключает нужное предупреждение, третья - восстанавливает сохраненное состояние. При этом состояние восстанавливается полностью - все сделанные уровнями выше изменения тоже восстанавливаются, предупреждения, выключенные по умолчанию, не включаются.
Выглядит страшно, но чего не сделаешь, чтобы не пропустить ту самую ошибку на миллиард.
0