添加消息以断言

发布于 2024-09-24 03:18:18 字数 322 浏览 9 评论 0原文

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

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

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

I'm looking for a way to add custom messages to assert statements.
I found this questions Add custom messages in assert? but the message is static there. I want to do something like this:

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

When the assertion fails I want the normal output plus for example "x was 100".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

小镇女孩 2024-10-01 03:18:18

你在这里运气不好。最好的方法是定义您自己的 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

仅当未定义非调试宏 NDEBUG 时,才会定义 ASSERT 宏。

然后你会像这样使用它:

ASSERT((0 < x) && (x < 10), "x was " << x);

这比你的用法简单一点,因为你不需要显式地字符串化 "x was " 和 x ,这就完成了由宏隐式地实现。

You are out of luck here. The best way is to define your own assert macro.

Basically, it can look like this:

#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

This will define the ASSERT macro only if the no-debug macro NDEBUG isn’t defined.

Then you’d use it like this:

ASSERT((0 < x) && (x < 10), "x was " << x);

Which is a bit simpler than your usage since you don’t need to stringify "x was " and x explicitly, this is done implicitly by the macro.

離人涙 2024-10-01 03:18:18

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

第一个是这样的:

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

还有:

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

第一个有效,因为内部括号表达式结果是“testbool”的值。
第二个有效,因为字符串的值将非零。

There are some old tricks to include messages without writing your own routines:

The first is this:

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

There is also:

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

The first one works, because the inside parens expression result is the value of 'testbool'.
The second one works, because the value of the string is going to be non-zero.

朦胧时间 2024-10-01 03:18:18

更好的选择是教调试器在失败时停止断言,然后您不仅可以检查 x 值,还可以检查包括调用堆栈在内的任何其他信息。或许,这才是你真正想要的。
这里提到了示例实现 在使用 C++ 编程时向您的合作程序员显示某些方法尚未在类中实现的方法

A better alternative is to teach the debugger to stop on assert when it fails, then you could examine not only the x value but any other information including call stack. Perhaps, this is what you are really looking for.
Sample implementation is mentioned here Ways to show your co-programmers that some methods are not yet implemented in a class when programming in C++

£噩梦荏苒 2024-10-01 03:18:18
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
#define ASSERT_WITH_MESSAGE(condition, message) do { \
if (!(condition)) { printf((message)); } \
assert ((condition)); } while(false)
柏拉图鍀咏恒 2024-10-01 03:18:18

扩展 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 宏在我的系统上输出的内容,只是带有附加的用户定义消息

Extending on Kondrad Rudolph's answer:

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

Output is...

Assertion failed: ((sum = 1 + 1) == 3), function foo, file foo.cpp, line 13.
got sum of 2, but expected 3
zsh: abort      ./a.out

which is similar to what the std::assert macro outputs on my system just with the additional user defined message

半边脸i 2024-10-01 03:18:18

为了完整起见,我在 C++ 中发布了一个包含 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() (on视窗,
    系统将提示用户附加调试器)
  • A(b)ort:立即调用 abort()

您可以在此处找到更多信息:

希望有帮助。

For the sake of completeness, I published a drop-in 2 files assert macro implementation in 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;
}

Will prompt you with:

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:

Where

  • (I)gnore: ignore the current assertion
  • Ignore (F)orever: remember the file and line where the assertion fired and
    ignore it for the remaining execution of the program
  • Ignore (A)ll: ignore all remaining assertions (all files and lines)
  • (D)ebug: break into the debugger if attached, otherwise abort() (on Windows,
    the system will prompt the user to attach a debugger)
  • A(b)ort: call abort() immediately

You can find out more about it there:

Hope that helps.

清晰传感 2024-10-01 03:18:18

是的,这是可能的。

要启用像 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 if ( 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

Yes, this is possible.

To enable expression like better_assert((0 < x) && (x < 10), std::string("x was ") + myToString(x));, we are supposed to have a corresponding macro in a form of

#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? \
(void)0 : print_assertion(std::cerr, \
"Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, \ 
" in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))

in which print_assertion is a proxy function to do the assertion. When the EXPRESSION is evaluated false, all the debug information, the __VA_ARGS__, will be dumped to std::cerr. This function takes arbitrary numbers of arguments, thus we should implement a variadic templated function:

template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
    out.precision( 20 );
    if constexpr( debug_mode )
    {
        (out << ... << args) << std::endl;
        abort();
    }
}

In the previous implementation, the expression (out << ... << args) << std::endl; make use of fold expression in C++17 (https://en.cppreference.com/w/cpp/language/fold); the constant expression debug_mode is related to the compilation options passed, which is can be defined as

#ifdef NDEBUG
    constexpr std::uint_least64_t debug_mode = 0;
#else
    constexpr std::uint_least64_t debug_mode = 1;
#endif

It also worth mentioning that the expression if constexpr( debug_mode ) makes use of constexpr if (https://en.cppreference.com/w/cpp/language/if) imported since C++17.

To wrap everything up, we have:

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

A typical test case demonstrating its usage can be:

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 );

This will produce something error message like:

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

And the full source code is available in this repo: https://github.com/fengwang/better_assert

初吻给了烟 2024-10-01 03:18:18

为了接受 Feng Wang 的回答,在较新版本的 C++ 中,任何内联内容都将被优化掉。因此,您可以在头文件中使用内联函数来完成所有工作。

inline constexpr void NOT_USED()
{
}

template <class T, class ...ARGS>
inline constexpr void NOT_USED(T && first, ARGS && ...args)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    static_cast<void>(first);
#pragma GCC diagnostic pop
    NOT_USED(args...);
}

template<typename ... ARGS>
void SAFE_ASSERT(bool test_result, ARGS &&... args)
{
#ifdef _DEBUG
    if(test_result)
    {
        (std::cerr << ... << args) << std::endl;
        abort();
    }
#else
    NOT_USED(test_result, args...);
#endif
}

一些评论:

  1. 如果未定义_DEBUG,该函数将变为空,因此可以 100% 优化

  2. 如果调用具有副作用的函数,非调试代码仍然有效:

     my_assert(c++ < --z, "#define 版本的行为不相似");
    

    这些副作用在这里清晰可见。但是,如果您调用一个函数,则很难知道该函数中是否发生了意外的事情。

    不过,有一些方法可以防止此类副作用的发生(示例)。但总而言之,在某些情况下,您需要调用函数进行测试,这可能会产生副作用,因此不需要在非调试代码中进行优化。

  3. 我使用 abort() 因为我知道可以正确停止调试器,std::terminate() 是现代系统中执行相同操作的 C++ 方式,但如果 std::terminate() 不适用于您的调试器,则 abort() 将会。

  4. NOT_USED() 是为了避免有关未使用的函数参数的警告(如果没有该警告,则可能会出现预期的错误)。

  5. 我在 snapdev 中有一个实现:请参阅safe_assert。 hnot_used.h

To take on Feng Wang answer, in newer versions of C++, anything inline is going to be optimized out. So you can have an inline function in a header file which does all the work.

inline constexpr void NOT_USED()
{
}

template <class T, class ...ARGS>
inline constexpr void NOT_USED(T && first, ARGS && ...args)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
    static_cast<void>(first);
#pragma GCC diagnostic pop
    NOT_USED(args...);
}

template<typename ... ARGS>
void SAFE_ASSERT(bool test_result, ARGS &&... args)
{
#ifdef _DEBUG
    if(test_result)
    {
        (std::cerr << ... << args) << std::endl;
        abort();
    }
#else
    NOT_USED(test_result, args...);
#endif
}

Some comments:

  1. If _DEBUG is not defined, the function becomes empty so it can be optimized out 100%

  2. If you call the function with a side effect, the non-debug code still works:

     my_assert(c++ < --z, "the #define versions do not behave similarly");
    

    These side effects are clearly visible here. However, if you call a function, it could be really difficult to know whether something happens in the function which was not otherwise expected.

    There are ways to prevent such side effects from happening, though (Example). But all in all, in some cases, you need to call a function for the test and it may have a side effect and therefore needs to not be optimized out in non-debug code.

  3. I use abort() because I know that stops the debugger properly, std::terminate() is the C++ way which in modern systems does the same thing, but if std::terminate() doesn't work for your debugger the abort() will.

  4. The NOT_USED() is to avoid warnings about unused function parameters (if you don't have that warning, you may end up with expected bugs).

  5. I have an implementation in snapdev: see safe_assert.h and not_used.h.

呆萌少年 2024-10-01 03:18:18

沿着康拉德鲁道夫的答案,你可以做得更简洁一点

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

,它也适用于C,

它使用你链接的问题的一些答案的一般思想,但宏允许它更灵活一点

going along with Konrad Rudolf's answer you can do it a bit more concise with

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

which also works in C,

it works using the general idea from some of the answers to the question you linked, but the macro allows it to be a little more flexible

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文