Утверждать и NDEBUG

Прочитав некоторые потоки о злоупотреблениях исключениями (в основном, говоря, что вы не хотите раскручивать стек, если неправильные функции предвзяты - возможно, сигнализируя, что вся ваша память повреждена или что-то такое же опасное) Я думаю об использовании assert() чаще. Раньше я только использовал assert() как инструмент отладки, и я думаю, что это так много программистов на С++. Я обеспокоен тем, что часть моей обработки ошибок отключена с помощью NDEBUG #define, введенного в сборку времени выполнения в какой-то момент в будущем. Есть ли способ обойти это, и у других возникли проблемы с этим (например, я должен беспокоиться об этом)?

Спасибо, Пат

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

//check later code won't crash the system
 if( buf.length() % 2 )
 return false;
 // do other stuff that shouldn't affect bufs length
 //copy 2 bytes into buf at a time, if length is odd then don't know 
 //what will happen so use assert to make sure it can't damage anything
 assert( !(buf.length() % 2) );
 for( i = 0; i != buf.length(); i += 2 )
 memcpy( buf + i, data, 2 );

edit2: обсуждение здесь: http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/80083ac31a1188da

6 ответов

Вы можете создать свой собственный assert вместо использования запаса C assert.h. Ваше утверждение не будет отключено.

Посмотрите, как assert() реализуется в /usr/include/assert.h(или где угодно). Это просто какая-то препроцессорная магия, которая в конечном итоге вызывает функцию "assert fail".

В наших встроенных средах мы все время заменяем assert().


Мне нравится определять мои собственные макросы утверждения. Я делаю тесты с двумя ASSERT всегда (даже для оптимизированных сборок), а DASSERT - только для отладочных сборников. Вероятно, вы хотите использовать ASSERT по умолчанию, но если что-то дорого тестировать, или утверждения внутри внутренних циклов чувствительных к производительности областей могут быть изменены на DASSERT.

Кроме того, помните, что утверждения должны использоваться только для абсолютно бессмысленных условий, которые указывают на логическую ошибку в вашей программе и из которой вы не можете восстановить. Это проверка правильности вашего программирования. Утверждения НИКОГДА не должны использоваться вместо обработки ошибок, исключений или надежности, и вы никогда не должны утверждать ничего, связанное с неправильным или неправильным вводом пользователя - такие вещи следует обрабатывать изящно. Утверждение - это просто контролируемый сбой, когда у вас есть возможность выводить дополнительную информацию об отладке.

Вот мои макросы:

/// ASSERT(condition) checks if the condition is met, and if not, calls
/// ABORT with an error message indicating the module and line where
/// the error occurred.
#ifndef ASSERT
#define ASSERT(x) \
 if (!(x)) { \
 char buf[2048]; \
 snprintf (buf, 2048, "Assertion failed in \"%s\", line %d\n" \
 "\tProbable bug in software.\n", \
 __FILE__, __LINE__); \
 ABORT (buf); \
 } \
 else // This 'else' exists to catch the user following semicolon
#endif
/// DASSERT(condition) is just like ASSERT, except that it only is 
/// functional in DEBUG mode, but does nothing when in a non-DEBUG
/// (optimized, shipping) build.
#ifdef DEBUG
# define DASSERT(x) ASSERT(x)
#else
# define DASSERT(x) /* DASSERT does nothing when not debugging */
#endif


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

Выпрыгивание из проблемы с исключением вряд ли помогает, оно не исправляет ошибку. Поэтому я бы рекомендовал реализовать свой собственный макрос ASSERT(), который:

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

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


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

Если предпосылки функции неверны, что бы вы хотели сделать? Как вы хотите сообщить об этом:

  • интерактивный пользователь?
  • ваш персонал службы поддержки

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

Однако, если вы все еще хотите пройти этот маршрут, вы можете поместить следующее в начало каждого исходного файла:

#undef NDEBUG

Я НЕ рекомендую это. Пожалуйста, не делай этого. Никто не поблагодарит вас: -)

Себ


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

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

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


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

licensed under cc by-sa 3.0 with attribution.