Добавление сообщения для утверждения
Hallo!
Я ищу способ добавления пользовательских сообщений для утверждения утверждений.
Я нашел эти вопросы Добавить пользовательские сообщения в assert?, но сообщение там статично. Я хочу сделать что-то вроде этого:
assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));
Когда утверждение терпит неудачу, я хочу, чтобы нормальный выход плюс, например, "x был 100".
Ответы
Ответ 1
Вам здесь не повезло. Лучший способ - определить собственный макрос assert
.
В принципе, это может выглядеть так:
#ifndef NDEBUG
# define ASSERT(condition, message) \
do { \
if (! (condition)) { \
std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
<< " line " << __LINE__ << ": " << message << std::endl; \
std::terminate(); \
} \
} while (false)
#else
# define ASSERT(condition, message) do { } while (false)
#endif
Это определит макрос assert
только в том случае, если не определено определение макроса NDEBUG
.
Затем вы используете его следующим образом:
ASSERT((0 < x) && (x < 10), "x was " << x);
Это немного проще, чем использование, так как вам не нужно явно выражать "x was "
и x
, это делается неявным образом с помощью макроса.
Ответ 2
Есть несколько старых трюков для включения сообщений без написания собственных подпрограмм:
Во-первых, это:
bool testbool = false;
assert(("this is the time", testbool));
Существует также:
bool testbool = false;
assert(testbool && "This is a message");
Первый работает, потому что результатом выражения внутреннего парса является значение 'testbool'.
Второй работает, потому что значение строки будет отличным от нуля.
Ответ 3
Лучшей альтернативой является научить отладчика останавливаться на утверждении, когда он терпит неудачу, тогда вы можете изучить не только значение x, но и любую другую информацию, включая стек вызовов. Возможно, это то, что вы действительно ищете.
Пример реализации здесь упоминается Способы показать вашим со-программистам, что некоторые методы еще не реализованы в классе при программировании на С++
Ответ 4
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
Ответ 5
Для полноты я опубликовал в С++ версию макроса assert-in 2 files:
#include <pempek_assert.h>
int main()
{
float min = 0.0f;
float max = 1.0f;
float v = 2.0f;
PEMPEK_ASSERT(v > min && v < max,
"invalid value: %f, must be between %f and %f", v, min, max);
return 0;
}
Вам будет предложено:
Assertion 'v > min && v < max' failed (DEBUG)
in file e.cpp, line 8
function: int main()
with message: invalid value: 2.000000, must be between 0.000000 and 1.000000
Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:
Где
- (I) gnore: игнорировать текущее утверждение
- Игнорировать (F) orever: запомнить файл и строку, в которой было запущено утверждение и
игнорировать его для оставшегося выполнения программы
- Игнорировать (A) ll: игнорировать все остальные утверждения (все файлы и строки)
- (D) ebug: перейдите в отладчик, если он подключен, иначе
abort()
(в Windows,
система предложит пользователю подключить отладчик)
- A (b) ort: вызов
abort()
немедленно
Вы можете узнать об этом больше:
Надеюсь, что это поможет.
Ответ 6
Да, это возможно
Чтобы включить выражение вроде better_assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));
мы должны иметь соответствующий макрос в виде
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? \
(void)0 : print_assertion(std::cerr, \
"Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, \
" in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
в котором print_assertion
является прокси-функцией для выполнения утверждения. Когда EXPRESSION
оценивается как false
, вся отладочная информация, __VA_ARGS__
, будет выгружена в std::cerr
. Эта функция принимает произвольное количество аргументов, поэтому мы должны реализовать шаблонную функцию:
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
В предыдущей реализации выражение (out <<... << args) << std::endl;
использовать выражение сгиба в С++ 17 (https://en.cppreference.com/w/cpp/language/fold); константное выражение debug_mode
связано с переданными параметрами компиляции, которые можно определить как
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
Стоит также упомянуть, что выражение if constexpr( debug_mode )
использует constexpr if (https://en.cppreference.com/w/cpp/language/if), импортированное с С++ 17.
Чтобы обернуть все, у нас есть:
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
#ifdef better_assert
#undef better_assert
#endif
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? (void)0 : print_assertion(std::cerr, "Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, " in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
Типичный тестовый пример, демонстрирующий его использование:
double const a = 3.14159265358979;
double const b = 2.0 * std::asin( 1.0 );
better_assert( a==b, " a is supposed to be equal to b, but now a = ", a, " and b = ", b );
Это выдаст что-то вроде сообщения об ошибке:
Assertion failure: a==b in File: test.cc in Line: 9 a is supposed to be equal to b, but now a = 3.1415926535897900074 and b = 3.141592653589793116
[1] 8414 abort (core dumped) ./test
И полный исходный код доступен в этом репо: https://github.com/fengwang/better_assert
Ответ 7
Продолжая на Кондрада Рудольфа ответ:
#include <iostream>
#ifdef NDEBUG
#define assert(condition, message) 0
#else
#define assert(condition, message)\
(!(condition)) ?\
(std::cerr << "Assertion failed: (" << #condition << "), "\
<< "function " << __FUNCTION__\
<< ", file " << __FILE__\
<< ", line " << __LINE__ << "."\
<< std::endl << message << std::endl, abort(), 0) : 1
#endif
void foo() {
int sum = 0;
assert((sum = 1 + 1) == 3, "got sum of " << sum << ", but expected 3");
}
int main () {
foo();
}
Вывод...
Assertion failed: ((sum = 1 + 1) == 3), function foo, file foo.cpp, line 13.
got sum of 2, but expected 3
zsh: abort ./a.out
что похоже на то, что выводит в моей системе макрос std :: assert только с дополнительным пользовательским сообщением
Ответ 8
соглашаясь с Конрадом Рудольфом, вы можете сделать это немного более лаконично с
#include <assert.h>
#include <stdio.h>
#define ASSERT(condition,...) assert( \
condition|| \
(fprintf(stderr,__VA_ARGS__)&&fprintf(stderr," at %s:%d\n",__FILE__,__LINE__)) \
);
который также работает в C,
он работает, используя общую идею из некоторых ответов на вопрос, который вы связали, но макрос позволяет ему быть немного более гибким.