向断言添加消息

时间:2010-09-22 09:22:48

标签: c++ logging debugging assert

喂!

我正在寻找一种向断言语句添加自定义消息的方法。 我发现了这个问题Add custom messages in assert?,但那里的信息是静态的。我想做这样的事情:

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

当断言失败时,我想要正常输出加上例如“x为100”。

8 个答案:

答案 0 :(得分:67)

你在这里运气不好。最好的方法是定义自己的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字符串化,这比您的使用简单一点,这是由宏隐式完成的。

答案 1 :(得分:14)

在没有编写自己的例程的情况下,有一些旧的技巧可以包含消息:

首先是:

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

还有:

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

第一个有效,因为内部表达结果是&#39; testbool&#39;的值。 第二个工作,因为字符串的值将是非零。

答案 2 :(得分:8)

更好的选择是教导调试器在失败时停止断言,然后您不仅可以检查x值,还可以检查包括调用堆栈在内的任何其他信息。也许,这就是你真正想要的。 这里提到了示例实现Ways to show your co-programmers that some methods are not yet implemented in a class when programming in C++

答案 3 :(得分:6)

#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)

答案 4 :(得分:2)

为了完整起见,我在C ++中发布了一个drop-in 2文件断言宏实现:

#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()

你可以在那里找到更多相关信息:

希望有所帮助。

答案 5 :(得分:0)

是的,这是可能的。

要启用像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)中的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 )从C ++ 17开始导入(https://en.cppreference.com/w/cpp/language/if)。

要包装所有内容,我们有:

#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

答案 6 :(得分:0)

扩展了Kondrad Rudolph的答案:

#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宏输出的内容相似,只是带有附加的用户定义消息

答案 7 :(得分:-1)

与Konrad Rudolf一起回答你可以用更简洁的方式做到这一点

ready

也适用于C,

它使用了您所链接问题的一些答案的一般概念,但宏允许它更灵活