Когда утверждения должны оставаться в производственном коде?

На comp.lang.c ++. Модерируется обсуждение того, следует ли сохранять в производственном коде утверждения, которые в C++ существуют только в отладочных сборках по умолчанию.

Очевидно, что каждый проект уникален, поэтому мой вопрос здесь не столько в том, следует ли сохранять утверждения, сколько в том , в каких случаях это рекомендуется / не очень хорошая идея.

Под утверждением я имею в виду:

  • Проверка во время выполнения, которая проверяет условие, которое, если оно ложно, выявляет ошибку в программном обеспечении.
  • Механизм, с помощью которого программа останавливается (возможно, после минимальной работы по очистке).

Я не обязательно говорю о C или C++.

Мое собственное мнение таково, что если вы программист, но не владеете данными (как в случае с большинством коммерческих настольных приложений), вам следует оставить их включенными, потому что ошибочное утверждение показывает ошибку, и вам не следует уходить. с ошибкой, с риском повреждения данных пользователя. Это заставляет вас тщательно тестировать перед отправкой и делает ошибки более заметными, что облегчает обнаружение и исправление.

Каково ваше мнение / опыт?

Ваше здоровье,

Карл

См. Связанный вопрос здесь


Ответы и обновления

Привет, Грэм,

Утверждение является ошибкой, чистым и простым, и поэтому с ним следует обращаться как с одним. Поскольку ошибка должна обрабатываться в режиме выпуска, вам действительно не нужны утверждения.

Вот почему я предпочитаю слово «ошибка», когда говорю об утверждениях. Это делает вещи намного яснее. Для меня слово «ошибка» слишком расплывчато. Отсутствующий файл - это ошибка, а не ошибка, и программа должна с этим бороться. Попытка разыменовать нулевой указатель является ошибкой, и программа должна признать, что что-то пахнет плохим сыром.

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


Немного не по теме, но важный момент в обсуждении.

В качестве предупреждения: если ваши утверждения попадают в отладчик, когда они терпят неудачу, почему бы и нет. Но существует множество причин, по которым файл не может существовать, которые полностью не контролируются вашим кодом: права чтения / записи, диск заполнен, USB-устройство отключено и т. Д. Поскольку вы не контролируете его, я считаю, что утверждения не правильный способ справиться с этим.

Карл


Томас,

Да, у меня есть Code Complete, и должен сказать, что я категорически не согласен с этим советом.

Допустим, ваш пользовательский распределитель памяти обнуляется и обнуляет кусок памяти, который все еще используется каким-то другим объектом. Я обнуляю указатель, который этот объект регулярно разыменовывает, и один из инвариантов состоит в том, что этот указатель никогда не является нулевым, и у вас есть несколько утверждений, чтобы убедиться, что он остается таким. Что делать, если указатель внезапно обнуляется. Вы просто if () обойдете это, надеясь, что это сработает?

Помните, что здесь мы говорим о коде продукта, поэтому нет необходимости взламывать отладчик и проверять локальное состояние. Это настоящая ошибка на машине пользователя.

Карл

Ответов (17)

Утверждение является ошибкой, чистым и простым, и поэтому с ним следует обращаться как с одним.

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

Основное преимущество утверждений, которое я вижу, - это условный разрыв - их гораздо проще настроить, чем просматривать окна VC для настройки чего-то, что занимает 1 строку кода.

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

Есть несколько исключений - например, PHP позволяет вам создать собственный обработчик ошибок утверждения, чтобы вы могли отображать пользовательские ошибки, вести подробный журнал и т. Д. Вместо того, чтобы просто выходить.

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

УТВЕРЖДЕНИЯ не являются ошибками и не должны рассматриваться как ошибки. Когда выдано утверждение, это означает, что есть ошибка в вашем коде или, альтернативно, в коде, вызывающем ваш код.

Есть несколько моментов, чтобы избежать включения утверждений в производственном коде: 1. Вы не хотите, чтобы ваш конечный пользователь видел сообщение типа «ASSERTION failed MyPrivateClass.cpp line 147. Конечный пользователь НЕ является вашим инженером по обеспечению качества. 2. ASSERTION может влиять на производительность

Однако есть одна веская причина оставить утверждения: ASSERTION может влиять на производительность и время, и, к сожалению, это иногда имеет значение (особенно во встроенных системах).

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

~ Ицик

Я считаю, что лучше всего обрабатывать все ошибки, которые находятся в области видимости, и использовать утверждения для предположений, которые мы утверждаем ИСТИННЫМИ.

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

Однако предположим, что ваша fopen () задокументирована как всегда возвращающая действительный дескриптор открытого файла. Вы открываете файл и передаете его функции readfile ().

Эта функция чтения файла в этом контексте и, вероятно, в соответствии с ее проектной спецификацией, может в значительной степени предполагать, что она получит действительный файл ptr. Таким образом, было бы расточительно добавлять код обработки ошибок для отрицательного случая в такой простой программе. Однако он должен, по крайней мере, задокументировать предположение, каким-то образом - каким-то образом - убедиться, что это действительно так, прежде чем продолжить его выполнение. Он не должен ДЕЙСТВИТЕЛЬНО предполагать, что он всегда будет действителен, если он вызван неправильно или, например, скопирован / вставлен в какую-то другую программу.

Итак, readfile () {assert (fptr! = NULL); ..} в этом случае подходит, в то время как полноценная обработка ошибок - нет (игнорируя тот факт, что фактическое чтение файла в любом случае потребует некоторой системы обработки ошибок).

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

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

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

Более интересный вопрос: когда на этапе тестирования вы отключаете утверждения?

Утверждения никогда не должны оставаться в производственном коде. Если конкретное утверждение кажется полезным в производственном коде, то это не должно быть утверждением; это должно быть время выполнения проверки ошибок, то есть что - то кодируется следующим образом: if( condition != expected ) throw exception .

Термин «утверждение» стал означать «проверку только во время разработки, которая не будет выполняться в полевых условиях».

Если вы начнете думать, что утверждения могут оказаться полезными, вы неизбежно начнете высказывать и другие опасные мысли, например, задаваться вопросом, действительно ли стоит делать какое-то утверждение. Нет утверждения, которое не стоит делать. Вы никогда не должны спрашивать себя: «Следует ли мне утверждать это или нет?» Вы должны только спросить себя: «Я что-то забыл сказать?»

Предположим, что фрагмент кода находится в разработке и попадает в утверждение, которое обычно запускается. В утверждении обнаружена ошибка! За исключением того, что это не так, потому что утверждение отключено.

Так что же теперь происходит? Либо программа (1) выйдет из строя неинформативным образом в точке, более удаленной от источника проблемы, либо (2) успешно запустится до завершения, что, вероятно, даст неправильный результат.

Ни один из сценариев не является привлекательным. Оставьте утверждения активными даже в производственной среде.

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

Однако я думаю, что это также требует, чтобы вы несколько изящно обрабатывали ошибки утверждения. Например, они должны приводить к общему типу диалога с возможностью (автоматически) сообщать о проблеме разработчикам, а не просто закрывать или прекращать работу программы. Кроме того, вы должны быть осторожны, чтобы не использовать утверждения для условий, которые вы действительно разрешаете, но, возможно, не нравитесь или считаете нежелательными. Эти условия должны обрабатываться другими частями кода.

Если вы хотите сохранить их, замените их обработкой ошибок. Нет ничего хуже, чем просто исчезающая программа. Я не вижу ничего плохого в том, чтобы рассматривать определенные ошибки как серьезные, но они должны быть направлены в тот раздел вашей программы, который оборудован для их устранения путем сбора данных, их регистрации и информирования пользователя о том, что в вашем приложении возникли нежелательные условия и выходит.

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

Предлагаю пример

file = create-some-file();
_throwExceptionIf( file.exists() == false, "FILE DOES NOT EXIST");

против

file = create-some-file();
ASSERT(file.exists());

Как приложение обработает утверждение? Я предпочитаю старый try catch метод работы с фатальными ошибками.

Позвольте мне процитировать Кодекс Стива МакКоннелла завершен. Раздел Утверждения - 8.2.

Обычно вы не хотите, чтобы пользователи видели сообщения утверждения в производственном коде; утверждения предназначены в первую очередь для использования во время разработки и сопровождения. Утверждения обычно компилируются в код во время разработки и компилируются из кода для производства.

Однако позже в том же разделе дается этот совет:

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

Я думаю, что если производительность не является проблемой, оставьте утверждение, а вместо того, чтобы отображать сообщение, пусть оно записывается в файл журнала. Я думаю, что этот совет также есть в Code Complete, но сейчас я его не нахожу.

Наше программное обеспечение сервера баз данных содержит как производственные, так и отладочные утверждения. Утверждения отладки - это просто так - они удаляются в производственном коде. Утверждения о производстве происходят только в том случае, если (а) существует какое-то условие, которое никогда не должно существовать, и (б) невозможно надежно выйти из этого состояния. Производственное утверждение указывает на то, что в программном обеспечении обнаружена ошибка или произошло какое-либо повреждение данных.

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

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

В большинстве случаев, когда я использую утверждение в java (ключевое слово assert), я автоматически добавляю некоторые производственные коды после. В зависимости от случая это может быть сообщение журнала, исключение ... или ничего.

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

В моем C++ я определяю REQUIRE (x), который похож на assert (x), за исключением того, что он выдает исключение, если утверждение не выполняется в сборке выпуска.

Поскольку неудавшееся утверждение указывает на ошибку, к нему следует относиться серьезно даже в сборке Release. Когда производительность моего кода имеет значение, я часто использую REQUIRE () для кода более высокого уровня и assert () для кода более низкого уровня, который должен выполняться быстро. Я также использую REQUIRE вместо assert, если условие сбоя может быть вызвано данными, переданными из кода, написанного третьей стороной, или повреждением файла (в оптимальном случае я бы специально разработал код, чтобы он вел себя хорошо в случае повреждения файла, но мы не всегда есть на это время.)

Они говорят, что вы не должны показывать эти сообщения assert конечным пользователям, потому что они их не поймут. Так? Конечные пользователи могут отправить вам электронное письмо со снимком экрана или текстом сообщения об ошибке, которое поможет вам отладить. Если пользователь просто говорит: «Произошел сбой», у вас нет возможности исправить это. Было бы лучше автоматически отправлять сообщения об ошибке утверждения, но это работает только в том случае, если (1) программное обеспечение работает на сервере, который вы контролируете / отслеживаете, или (2) у пользователя есть доступ в Интернет, и вы можете получить его разрешение на отправку отчет об ошибке.

Утверждения - это комментарии, которые не устаревают. Они документируют, какие теоретические состояния предназначены, а какие не должны возникать. Если код изменен так, что в нем указано разрешенное изменение, разработчик вскоре будет проинформирован и должен обновить утверждение.

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

если не стоит проводить измерения, чтобы доказать, что он более эффективен, тогда не стоит жертвовать ясностью ради игры », - Стив МакКоннелл, 1993

http://c2.com/cgi/wiki?ShipWithAssertionsOn