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

166

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

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

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

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

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

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

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

Ура,

деревенщина

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


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

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

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

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

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


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

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

деревенщина


Томас,

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

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

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

деревенщина

неизвестно
источник
3
В Software Engineering SE есть интересная связанная статья (хотя обсуждение сосредоточено на c ++): должны ли быть утверждения в сборках релиза
sonny

Ответы:

84

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

MSalters
источник
16
@jkschneider: модульные тесты предназначены для тестирования вещей в рамках кода программы. Утверждения предназначены для обеспечения того, чтобы предположения, на которых основан код, действительно действительны в действительности до того, как код продолжит обработку в соответствии с этими предположениями. Это «документация» в том смысле, что, если окажется, что это не так, программа прервет работу, укажет предположение и укажет, что это предположение не подтвердилось. Вы также можете прочитать утверждение как документацию этого в коде, конечно.
2
Это лучший ответ, потому что он относится к утверждениям к комментариям, что является полезным способом думать о них. Они на шаг от комментариев, потому что они постоянно тестируются на машинах во время разработки, но они всегда должны быть значимыми для читателей-людей. Как и комментарии, они не должны быть частью логики или окончательного исполнения. Как и комментарии, вы можете оставить их или удалить их в зависимости от того, скомпилирован ли язык или интерпретирован, ваши планы развертывания, стратегия запутывания и т. Д. Я видел один случай, когда комментарий действительно вызывал ошибку, но это было странный
DaveWalley
1
Более конкретно, утверждения являются информационными и не функциональными . Утверждение само по себе не влияет на ход программы или результаты. Исключение, с другой стороны, изменяет ход программы и, следовательно, приводит к результатам.
йо
59

Позвольте мне процитировать полный код Стива Макконнелла. Раздел «Утверждения» - 8.2.

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

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

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

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

Томас Оуэнс
источник
7
Я думаю, что вторая цитата из Code Complete означает, что у вас должен быть assert - который будет скомпилирован в производственном коде - и у вас также должно быть «if (! Condition) {AttemptGracefulRecovery ();}», т.е. don не позволяйте нарушению программных инвариантов завершить работу программы.
йо
34

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

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

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

David Cary
источник
11
На самом деле, худшее, что происходит, это когда происходит сбой кода из-за того, что НЕ является утверждением. Если код определенно рухнет позже с вероятностью 100%, тогда утверждение обязательно должно быть. Если вы разыграете указатель, то вам придется неявно утверждать, что он не равен нулю раньше. Если вы делите на число, вы утверждаете, что это не ноль. Удалите утверждения, и все места крушения НЕ УКАЗАНЫ. Реальная проблема заключается не в том, чтобы структурировать программу так, чтобы подсистемы давали сбой и перезапускались сторожевым таймером.
Роб
5
assert ref != null;отличается от того, что if (ref == null) throw new IllegalArgumentException();вы не должны использовать первое для предварительных условий, которые могут быть ложными. Вы должны использовать assertдля вещей, которые не могут быть ложными. Например, int i = -1 * someNumber; i = i * i;потом напомнить людям, что iэто позитивно,assert i > 0;
Капитан Мэн
1
«На самом деле, худшее, что случается, это когда происходит сбой кода из-за чего-то, что НЕ является утверждением. Если код определенно завершится позже с вероятностью 100%, тогда утверждение обязательно должно быть». - это ложная дихотомия.
Роб Грант
2
@RobertGrant: для многих программ сбой - далеко не самое страшное, что может случиться. Для программы, которая должна проверять правильность конструкции здания или моста, ошибочное сообщение о том, что проект является надежным, может быть едва ли не худшим, что он может сделать. Для программы, которая работает с внешним миром, но имеет доступ только для чтения к конфиденциальным данным, утечка этих данных может быть хуже, чем что-либо еще, что может сделать программа. Представление о том, что авария без какого-либо значимого указания на причину - это «худшее, что может произойти», игнорирует многие опасности, которые намного хуже.
суперкат
@supercat Я не согласен с комментарием, который цитирую.
Роб Грант
21

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

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

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

MiguelMunoz
источник
4
Я думаю, что утверждения никогда не должны быть включены в производственный код. Утверждения НЕ являются ошибками, они предназначены для разработчиков. Утверждения должны жить только в тестовом коде. Сбой приложения происходит из-за недопустимого и ошибочного развития утверждения. Разработчики должны приложить дополнительные усилия, чтобы корректно обрабатывать ошибки.
Икснаэ
9
Если это неизбежно, вы потерпите крах, учитывая нулевой указатель, переданный в fn; нет никакого выбора в том, чтобы иметь дело с этим явно. У вас либо есть способ изящно обработать условие (потому что оно может исходить от ввода из внешнего мира), либо вы терпите крах в ДОКУМЕНТАЦИОННОМ месте с утверждением, а не в случайном месте, которое могло повредить вещи на этом пути. то, как обрабатывается assert, должно приниматься за модуль. Возможно, ваш сторожевой таймер перезапускает процесс, или вы стираете кусок памяти для этого модуля, чтобы сбросить его в исходное состояние (программный объект «перезагрузка»).
Роб
1
Я думаю, что это ограниченный взгляд на использование утверждений. Я всегда регистрирую утверждения как в консольном, так и в облачном хранилище, и оставляю их в работе. Оставляя утверждения, подтверждает, что мои предположения остаются верными даже в производственном коде и в производственном использовании. Тот факт, что код несколько раз успешно выполнялся при отладке с включенными утверждениями, не означает, что пользователи не найдут способ передать разные значения по одному и тому же пути кода.
SafeFastExpressive
Весь смысл утверждения assert заключается в том, что вы можете включать или выключать проверки. Если вы оставляете их включенными в производство, зачем использовать утверждение assert?
MiguelMunoz
1
Утверждения часто замедляют работу системы. Поскольку они не предназначены для производства, они могут быть медленными и неэффективными, что может быть необходимо для выполнения определенных тестов. Например, Microsoft однажды добавила функцию быстрого пересчета в Excel. Когда одна ячейка изменилась, эта функция ограничивала пересчет только ячейками, которые в ней нуждались. Они проверили это с утверждением, которое пересчитало всю таблицу и сравнило результаты. Это привело к тому, что версия для разработки работала очень медленно, но она также устраняла множество ошибок. Когда они выпустили эту функцию, она оказалась очень надежной.
MiguelMunoz
16

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

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

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

Майк Накис
источник
6

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

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

Андерс Сандвиг
источник
На мой взгляд, основная цель производственного утверждения заключается в том, чтобы обеспечить аварийную поддержку: продолжение программы может нанести достаточно серьезный вред, предотвращение которого важнее всего, что может делать программа. Было бы неплохо иметь хорошее сообщение об ошибке, если это возможно, но это имеет второстепенное значение.
суперкат
5

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

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

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

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

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

bruceatk
источник
2

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

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

Стив М
источник
2

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

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

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

Грэм Перроу
источник
5
Я бы назвал ваше «производственное утверждение» «исключением» и закодировал бы его как таковой.
DaveWalley
1
Вы, вероятно, правы, но продукт изначально был написан на C. Даже когда мы изменили его на C ++, компиляторы, которые мы использовали на некоторых наших платформах, должным образом не поддерживали исключения. Большая часть старого кода не была переписана на C ++, поэтому эти утверждения все еще используются.
Грэм Перроу
1

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

Уэстон
источник
Утверждение: констатируйте факт или веру Они предназначены не для тестирования (только), а для того, чтобы заявить, что вы считаете верным (то есть ваши предположения), чтобы ваша программа не продолжалась, если ваши предположения по какой-то причине неверны. Например, это правильное использование утверждений: assert (pow (1,0) <1). Это не совсем подходящее место для проверки ошибок, потому что, если это не так, то почти вся современная математика ошибочна, и ... ну, как бы вы начали с этим справляться? Обработка этого неверного предположения выходит за рамки программы; вы принимаете это на веру. Но вы все равно проверяете.
1

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

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

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

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

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

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

Ли
источник
1

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

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

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

Убийца Сурок
источник
0

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

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

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

против

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

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

РОО
источник
2
Исключения составляют необычные ситуации, с которыми вы ожидаете столкнуться в рабочем приложении, как в вашем примере здесь. Утверждения предназначены для ситуаций, с которыми вы ожидаете никогда не столкнуться. Так что, если вы столкнулись с ними, в вашем коде должна быть ошибка. В вашем примере и исключении явно правильный подход. Но утверждения по-прежнему очень полезны для выявления ошибок.
MiguelMunoz
0

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

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

никола
источник
0

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

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

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

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

~ Ицик

Ицхак Яром
источник
Нет, утверждения для предполагаемых фактов. Если наш код возвращает 27, когда предполагается, что он ВСЕГДА вернет 25, проблема также может заключаться в ошибке в наших физических предположениях о юниверсе: возможно, эти два бита переключились на 5 возможных значений впервые в истории вычислений. Подтверждение того, что ваш код все еще работает в соответствии с предположениями, для которых он был написан. Если физика выходит из окна, ваш код должен заметить, оставить диск в покое и выйти, пока он впереди;) Но да, это не ошибка в коде, и какую обработку ошибок вы могли бы сделать?
Позвольте мне уточнить мое мнение: 1. Утверждение проверить наши предположения. Если наши предположения ошибочны, это означает, что в НАШЕМ коде есть ошибка. 2. Наш код не должен утверждать использование нашего кода. функция не должна давать сбой при утверждении, если что-то не так во входных данных от пользователя. Мы вернем ошибку, и пользователь должен обработать (он может утверждать успех) 3. Я предпочитаю оставить утверждение в производстве - но поведение, вероятно, будет изменено. Я согласен, что нет соответствующей обработки ошибок. Неудачное утверждение == ошибка .. но система может перезапустить себя вместо остановки и ожидания перезагрузки.
Ицхак Яром
1
это НЕ обязательно означает, что есть ошибка. Например, многие проекты, в которых я участвую, содержат список предполагаемых фактов в документации. Эти предположения существуют для того, чтобы защитить код разработчиков от называния ошибками, когда деловые люди могли сказать им что-то не так, или когда третьи стороны не получают информацию о конкретных переменных, например. Утверждения могут быть использованы для проверки того, что программа должна / не должна работать, что сторонние системы верны, а не только, правильны ли ИТ.
-8

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

Так как ошибка должна быть обработана в режиме релиза, тогда вам не нужны утверждения.

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

graham.reeds
источник
2
Использование утверждений в качестве условных точек останова действительно раздражает по нескольким причинам. Наиболее важным является то, что эти утверждения сбивают с толку других разработчиков в команде - как они узнают, если это ошибка, когда это утверждение сработало или это кто-то оставил свою условную точку останова в коде?
LEGO
Не было бы, если бы утверждений не было в коде в первую очередь. И если бы вы использовали их только для мониторинга кода, вы бы их увидели (если только вы не проверяли их в дереве исходных текстов). Я работал в местах, где есть практически все. Необходимость щелкать около 60 раз при запуске программы, поскольку сервер справочной документации недоступен, очень быстро утомляет.
graham.reeds