Каков наилучший способ реализации проверки утверждений в C++?
Под этим я подразумеваю, что мне нужно сделать, чтобы иметь полезные утверждения в моем коде?
MFC довольно легко, я просто использую ASSERT(что-то).
Что не так, как в MFC?
Редактировать: возможно ли остановить прерывание assert в assert.c, а не в моем файле с именем assert()?
Изменить: какая разница между <assert.h>
& <cassert>
?
Принятый ответ: В этом посте множество отличных ответов, я хотел бы принять более одного ответа (или кто-то объединит их все). Таким образом, ответ получает Ферруччо (за первый ответ).
11 ответов
#include <cassert>
assert(something);
и для проверки во время компиляции статические утверждения Boost очень полезны:
#include <boost/static_assert.hpp>
BOOST_STATIC_ASSERT(sizeof(int) == 4); // compile fails if ints aren't 32-bit
Это зависит от того, ищете ли вы что-то, что работает вне Visual C++. Это также зависит от того, какое утверждение вы ищете.
Есть несколько типов утверждений:
препроцессор
Эти утверждения выполняются с использованием директивы препроцессора.#error
Утверждения препроцессора оцениваются только на этапе предварительной обработки и поэтому бесполезны для таких вещей, как шаблоны.Время выполнения
Эти утверждения сделаны с использованиемassert()
функция, определенная в<cassert>
Утверждения времени выполнения оцениваются только во время выполнения. И, как указал BoltBait, они не компилируются, еслиNDEBUG
макрос был определен.статический
Эти утверждения сделаны, как вы сказали, с помощьюASSERT()
макрос, но только если вы используете MFC. Я не знаю другого способа сделать статические утверждения, который является частью стандарта C/C++, однако библиотека Boost предлагает другое решение:static_assert
,static_assert
Функция из библиотеки Boost будет добавлена в стандарт C++ 0x.
В качестве дополнительного предупреждения assert()
Функция, предложенная Ферруччо, отличается от MFC ASSERT()
макро. Первое является утверждением времени выполнения, а второе - статическим утверждением.
Надеюсь, это поможет!
Утверждается, что (обычно) только отладка
Проблема с "assert" заключается в том, что он обычно находится в отладочных двоичных файлах и что некоторые разработчики используют их так, как если бы код все еще работал.
Само по себе это не зло, поскольку предполагается, что код подвергается интенсивному тестированию, и, следовательно, ошибки, вызывающие утверждение, обязательно будут обнаружены и удалены.
Но иногда (в большинстве случаев?) Тесты не так интенсивны, как хотелось бы. Я не буду говорить о старой работе, где нам приходилось кодировать до самой последней минуты (не спрашивайте... Иногда менеджеры просто... хм...)... Какой смысл утверждать вас добавить к коду, который будет скомпилирован и передан в виде Release Binary клиенту в следующую минуту?
Заявить в (некоторых) реальных приложениях
В нашей команде нам нужно было что-то, чтобы обнаружить ошибку, и в то же время что-то еще, чтобы обработать ошибку. И нам это понадобилось, возможно, в 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), но везде это хорошо. И факты того, что можно использовать любую предпочтительную обработку ошибок и то, что она может быть активирована по желанию, либо на компьютере разработчика, либо на тестере, либо даже на пользователе, весьма полезны.
Чтобы ответить на вопрос в вашем втором "редактировании":
Основное использование Assert
#include <cassert>
/* Some code later */
assert( true );
Заметки о лучших практиках
Утверждения используются для определения состояний времени выполнения, которые должны быть истинными. В результате они компилируются в режиме выпуска.
Если у вас есть ситуация, когда вы хотите, чтобы утверждение всегда срабатывало, вы можете передать ему false. Например:
switch ( someVal ):
{
case 0:
case 1:
break;
default:
assert( false ); /* should never happen */
}
Также возможно передать сообщение через assert:
assert( !"This assert will always hit." );
Зрелые кодовые базы часто расширяют функциональность assert. Некоторые из распространенных расширений включают в себя:
- Переключение утверждает для каждого модуля для локализации тестирования.
- Создание дополнительного макроса assert, который компилируется в большинстве отладочных сборок. Это желательно для кода, который вызывается очень часто (миллионы раз в секунду) и вряд ли будет неправильным.
- Предоставление пользователям возможности отключить текущий запрос подтверждения, все утверждения в модуле компиляции или все утверждения в кодовой базе. Это предотвращает запуск доброкачественных утверждений, создавая непригодные сборки.
Чтобы разорвать внутри файла, который вызвал assert, вы можете использовать пользовательский макрос, который генерирует исключение или вызывает __debugbreak
:
#define MYASSERT(EXPR, MSG) if (!(EXPR)) throw MSG;
Или же:
#define MYASSERT(EXPR) if (!(EXPR)) __debugbreak();
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());
Существует более продвинутая библиотека с открытым исходным кодом под названием ModAssert, которая имеет утверждения, которые работают как на Visual C++, так и на gcc. Вероятно, также на других компиляторах, не знаю точно. Это займет некоторое время, чтобы изучить его, но если вы хотите получить хорошие утверждения, которые не зависят от MFC, посмотрите на них. Это на http://sourceforge.net/projects/modassert/
Чтобы ответить на третий вопрос задающего: первая причина, по которой мы используем "cassert" вместо "assert.h", заключается в том, что в случае C++ существует допущение, что компилятор C++ может не сохранять описания функций в коде файлы, но в DLL или в самом компиляторе. Во-вторых, в функции могут быть внесены незначительные изменения, чтобы облегчить различия между C и C++, как в настоящее время, так и в будущем. Поскольку assert.h является библиотекой C, в C++ предпочтение отдается использованию "cassert".
Используйте intellisense, чтобы открыть его в visual studio (щелкните правой кнопкой мыши)
// cassert standard header
#include <yvals.h>
#include <assert.h>
yvals.h - это материал для Windows. поэтому, что касается самого assert(), два способа его включения идентичны. это хорошая практика, чтобы использовать <cxxx>
потому что часто это не так просто (перенос пространства имен и, возможно, другая магия)
Это ломается на сайте звонящего для меня...
Вот статья, объясняющая, почему вы не хотите писать этот макрос самостоятельно.
Вот моя последняя итерация средства утверждения в C++: http://pempek.net/articles/2013/11/17/cross-platform-cpp-assertion-library/
Это библиотека из двух файлов, которую вы можете легко добавить в свой проект.