Каков наилучший способ реализации проверки утверждений в C ++? - PullRequest
19 голосов
/ 07 октября 2008

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

MFC довольно просто, я просто использую ASSERT (что-то).

Что такое способ без MFC?

Редактировать: Можно ли остановить прерывание assert в assert.c, а не в моем файле с именем assert ()?

Редактировать: В чем разница между <assert.h> & <cassert>?

Принятый ответ: Множество отличных ответов в этом посте, я хотел бы принять более одного ответа (или кто-то объединит их все) Таким образом, ответ получает Ферруччо (за первый ответ).

Ответы [ 11 ]

29 голосов
/ 07 октября 2008
#include <cassert>

assert(something);

и для проверки во время компиляции статические утверждения Boost очень полезны:

#include <boost/static_assert.hpp>

BOOST_STATIC_ASSERT(sizeof(int) == 4);  // compile fails if ints aren't 32-bit
13 голосов
/ 07 октября 2008

Это зависит от того, ищете ли вы что-то, что работает вне Visual C ++. Это также зависит от того, какое утверждение вы ищете.

Существует несколько типов утверждений:

  1. Препроцессор
    Эти утверждения выполняются с использованием директивы препроцессора #error
    Утверждения препроцессора оцениваются только на этапе предварительной обработки и поэтому бесполезны для таких вещей, как шаблоны.

  2. Время выполнения
    Эти утверждения выполняются с использованием функции assert(), определенной в <cassert>
    Утверждения времени выполнения оцениваются только во время выполнения. И, как указал BoltBait, они не компилируются, если был определен макрос NDEBUG.

  3. Статический
    Эти утверждения выполняются, как вы сказали, с помощью макроса ASSERT(), но только если вы используете MFC. Я не знаю другого способа сделать статические утверждения, который является частью стандарта C / C ++, однако библиотека Boost предлагает другое решение: static_assert.
    Функция static_assert из библиотеки Boost будет добавлена ​​в стандарт C ++ 0x .

В качестве дополнительного предупреждения функция assert(), предложенная Ферруччо, не работает так же, как макрос MFC ASSERT(). Первый является утверждением времени выполнения, а второй - статическим.

Надеюсь, это поможет!

10 голосов
/ 08 октября 2008

Assert (обычно) только для отладки

Проблема с "assert" состоит в том, что он обычно находится в отладочных двоичных файлах, и что некоторые разработчики используют их так, как если бы код все еще работал.

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

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

Утверждение в (некоторых) реальных приложениях

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

Assert обнаружит и обработает ошибку только при отладочной сборке.

Поэтому мы добавили вместо этого макрос XXX_ASSERT, а также макрос XXX_RAISE_ERROR.

Макрос XXX_ASSERT будет делать то же самое, что и макрос ASSERT, но он будет построен как в Debug, так и в Release. Его поведение (запись журнала, открытие окна сообщений, ничего не делать и т. Д.) Может контролироваться файлом .INI, а затем он может прервать / закрыть приложение.

Это использовалось как:

bool doSomething(MyObject * p)
{
   // If p is NULL, then the app will abort/exit
   XXX_ASSERT((p != NULL), "Hey ! p is NULL !") ;

   // etc.
}

Макрос XXX_RAISE_ERROR только "регистрирует" ошибку, но не пытается ее обработать. Это означает, что он может записать сообщение в файл и / или открыть MessageBox с сообщением, кнопкой для продолжения и другой для запуска сеанса отладки (согласно конфигурации файла .INI). Это использовалось как:

bool doSomething(MyObject * p)
{
   if(p == NULL)
   {
      // First, XXX_RAISE_ERROR will alert the user as configured in the INI file
      // perhaps even offering to open a debug session
      XXX_RAISE_ERROR("Hey ! p is NULL !") ;
      // here, you can handle the error as you wish
      // Than means allocating p, or throwing an exception, or
      // returning false, etc.
      // Whereas the XXX_ASSERT could simply crash.
   }

   // etc.
}

Через год после их появления в наших библиотеках используется только XXX_RAISE_ERROR. Конечно, его нельзя использовать в критических по времени частях приложения (для этого у нас есть XXX_RAISE_ERROR_DBG), но везде это хорошо. И тот факт, что можно использовать любую предпочтительную обработку ошибок и то, что ее можно активировать по желанию, либо на компьютере разработчика, либо на тестере, либо даже на пользователе, весьма полезен.

9 голосов
/ 07 октября 2008

Чтобы ответить на вопрос в вашем втором «редактировании»:

- заголовок C

- заголовок стандартной библиотеки C ++ ... обычно он включает

6 голосов
/ 07 октября 2008

Основное использование Assert

#include <cassert>

/* Some code later */
assert( true );

Примечания по лучшей практике

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

Если у вас есть ситуация, когда вы хотите, чтобы assert всегда срабатывал, вы можете передать ему false. Например:

switch ( someVal ):
{
case 0:
case 1:
  break;
default:
  assert( false ); /* should never happen */
}

Также возможно передать сообщение через assert:

assert( !"This assert will always hit." );

Зрелые кодовые базы часто расширяют функциональность assert. Некоторые из распространенных расширений включают в себя:

  • Переключение утверждает для каждого модуля для локализации тестирования.
  • Создание дополнительного макроса assert, который компилируется в большинстве отладочных сборок. Это желательно для кода, который вызывается очень часто (миллионы раз в секунду) и вряд ли будет неправильным.
  • Предоставление пользователям возможности отключить текущий запрос на попадание, все утверждения в модуле компиляции или все утверждения в кодовой базе. Это предотвращает запуск доброкачественных утверждений, создавая непригодные сборки.
6 голосов
/ 07 октября 2008

Для разбиения внутри файла, который вызвал assert, вы можете использовать пользовательский макрос, который выдает исключение или вызывает __debugbreak:

#define MYASSERT(EXPR, MSG) if (!(EXPR)) throw MSG;

Или:

#define MYASSERT(EXPR) if (!(EXPR)) __debugbreak();
5 голосов
/ 07 октября 2008

Microsoft CRT утверждает

#include <crtdbg.h>
#include <sstream>
...
// displays nondescript message box when x <= 42
_ASSERT(x > 42);
// displays message box with "x > 42" message when x <= 42
_ASSERTE(x > 42);
// displays message box with computed message "x is ...!" when x <= 42
_ASSERT_EXPR(
   x > 42, (std::stringstream() << L"x is " << x << L"!").str().c_str());
3 голосов
/ 17 ноября 2009

Существует более продвинутая библиотека с открытым исходным кодом под названием ModAssert, которая имеет утверждения, которые работают как на Visual C ++, так и на gcc. Вероятно, также на других компиляторах, не знаю наверняка. Это займет некоторое время, чтобы выучить это, но если вы хотите хорошие утверждения, которые не зависят от MFC, посмотрите на них. Это на http://sourceforge.net/projects/modassert/

1 голос
/ 20 ноября 2014

Чтобы ответить на третий вопрос автора: Первая причина, по которой мы используем «cassert» вместо «assert.h», заключается в том, что в случае C ++ существует допущение, что компилятор C ++ может хранить описания функций не в файлах кода, а в DLL или в самом компиляторе. Во-вторых, в функции могут быть внесены незначительные изменения, чтобы облегчить различия между C и C ++, как в настоящее время, так и в будущем. Поскольку assert.h является библиотекой C, в C ++ предпочтение отдается использованию «cassert».

1 голос
/ 11 февраля 2014

Вот моя последняя итерация средства подтверждения в C ++: http://pempek.net/articles/2013/11/17/cross-platform-cpp-assertion-library/

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

...