отстаивать и NDEBUG - PullRequest
       13

отстаивать и NDEBUG

6 голосов
/ 23 декабря 2008

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

Спасибо, Пат

Edit: Суть потоков, которые я читал, заключалась в том, что если ваше приложение действительно содержит ошибки, то раскручивание стека может повредить систему, например, если деструктор записал что-то в файл и дескриптор файла был поврежден. Я не предлагаю использовать 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 ]

8 голосов
/ 23 декабря 2008

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

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

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

7 голосов
/ 23 декабря 2008

Мне нравится определять свои собственные макросы утверждений. Я делаю два теста 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's 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
4 голосов
/ 23 декабря 2008

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

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

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

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

1 голос
/ 23 декабря 2008

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

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

  • интерактивный пользователь?
  • ваш вспомогательный персонал?

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

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

#undef NDEBUG

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

Себ

1 голос
/ 23 декабря 2008

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

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

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

0 голосов
/ 23 декабря 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...