Добавление сообщения для подтверждения - PullRequest
38 голосов
/ 22 сентября 2010

Привет!

Я ищу способ добавить пользовательские сообщения в утверждения assert.Я нашел эти вопросы Добавить пользовательские сообщения в assert? , но сообщение там статично.Я хочу сделать что-то вроде этого:

assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));

Когда утверждение не удается, я хочу нормальный вывод плюс, например, "х был 100".

Ответы [ 8 ]

68 голосов
/ 22 сентября 2010

Вам не повезло здесь.Лучший способ - определить собственный макрос 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 явно, это делается неявно макросом.

14 голосов
/ 29 ноября 2016

Есть несколько старых приемов, которые включают сообщения без написания собственных подпрограмм:

Первое:

bool testbool = false;
assert(("this is the time", testbool));

Также есть:

bool testbool = false;
assert(testbool && "This is a message");

Первый работает, потому что результатом внутреннего выражения parens является значение 'testbool'.Второй работает, потому что значение строки будет ненулевым.

8 голосов
/ 22 сентября 2010

Лучшая альтернатива - научить отладчик останавливаться на assert в случае его сбоя, тогда вы можете проверить не только значение x, но и любую другую информацию, включая стек вызовов. Возможно, это то, что вы действительно ищете. Здесь приведен пример реализации Способы показать вашим со-программистам, что некоторые методы еще не реализованы в классе при программировании на C ++

6 голосов
/ 22 сентября 2010
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
2 голосов
/ 28 февраля 2014

Для полноты картины я опубликовал 2 раскрывающих файла реализации макросов утверждения в C ++:

#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) или всегда: запомните файл и строку, где сработало утверждение, и игнорировать его для оставшегося выполнения программы
  • Игнорировать (A) ll: игнорировать все оставшиеся утверждения (все файлы и строки)
  • (D) ebug: взломать отладчик, если он подключен, в противном случае abort() (в Windows, система предложит пользователю подключить отладчик)
  • A (b) ort: немедленно позвоните abort()

Подробнее об этом можно узнать здесь:

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

0 голосов
/ 11 июня 2019

Расширение на ответ Кондрада Рудольфа:

#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 выводит в моей системе толькодополнительное пользовательское сообщение

0 голосов
/ 13 сентября 2018

Да, это возможно.

Чтобы включить выражение типа 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; использует выражение сгиба в C ++ 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, если (https://en.cppreference.com/w/cpp/language/if) импортировано с C ++ 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

0 голосов
/ 10 июля 2017

, следуя ответу Конрада Рудольфа, вы можете сделать его более кратким с

#include <assert.h>
#include <stdio.h>
#define ASSERT(condition,...) assert( \
    condition|| \
    (fprintf(stderr,__VA_ARGS__)&&fprintf(stderr," at %s:%d\n",__FILE__,__LINE__)) \
);

, который также работает на C,

, он работает, используя общую идею из некоторых ответовна вопрос, который вы связали, но макрос позволяет ему быть более гибким

...