如何制作健壮的断言?

时间:2014-02-23 21:16:13

标签: c++ assert

我想实现这种行为:

  • 当程序在调试模式下运行时,assert_robust(expression, commands)的工作方式与经典assert(expression)
  • 完全相同
  • 当程序在发布模式下运行时,如果表达式为false,assert_robust(expression, commands)会执行一些commands

这可以这样做:

#ifdef NDEBUG
    #define assert_robust(expression, command) if (!(expression)) command;
#else
    #define assert_robust(expression, command) assert(expression); 
#endif

这可以用这种方式使myfunction容错:

char myfunction(const string& s, int i)
{
    assert_robust(i >= 0, return '\0');

    /* Normal code */
}

这项工作很好,但是如何使宏assert_robust支持多个(任意)数量的命令? (最好采用标准的C ++方式)

另一个问题是:

在发布中严格调试和仁慈是好事吗?

编辑:我的想法为什么这样做是因为如果它是程序中的一个错误实际上要好得多,程序有时保持有点奇怪而不是它崩溃和用户失去他们的数据

3 个答案:

答案 0 :(得分:3)

更有趣的问题是第二个问题:

  

在发布中严格调试和仁慈是好事吗?

我的经验是,在调试和发布版本中有不同的行为是一个可怕的想法。您正在注册生产中无法在调试版本中重现的问题,因为两者的行为不同。

除此之外,如果您在调试模式中首先断言,您可能声称这不会成为问题,断言应该用于标记编程问题,即您无法安全恢复的情况。如果您可以在发布模式下安全恢复,为什么要在DEBUG中断言?如果你不能,你是否愿意以某种方式摆弄生产数据,你不明白它会做什么?

答案 1 :(得分:0)

我不认为以这种方式使用断言是个好主意。通常,如果希望谓词始终为true,则使用断言,因为它是关键代码的一部分。如果不是真的,那么显然存在一个大问题,并且中止是合理的。但是越来越多的人使用断言像普通的错误检查一样进行调试。在这种情况下,它足以在释放模式下完全禁用它。它认为你应该决定采用这两种方法之一。

但是如果你想在中止之前运行某种紧急命令,你可以使用C ++ 11中新的lambda函数:

void ASSERT(int expression, std::function<void()> func) {
    if(!expression) {
        if (func) func();
        abort();
    }
}

您可以像这样使用它:

ASSERT(a >= 0, []() { std::cerr << "ERROR" << std::endl;});

或者:

ASSERT(a >= 0, [this]() { this->terminate(); });

或者:

ASSERT(a >= 0, nullptr);

答案 2 :(得分:0)

如果这不是一个好主意的问题,你可以使用你的宏在do-while(0);循环中包装多个命令。

#ifdef NDEBUG
    #define assert_robust(expression, command) if (!(expression)) \
                                                 do{command;} while(0)
#else
    #define assert_robust(expression, command) assert(expression)
#endif

另请注意,我没有在宏的末尾包含分号。如果将它们包含在宏中,那么就像

assert_robust(cond1, command1) /* no semicolon here, no problem */
assert_robust(cond2, command2) /* no semicolon here, no problem */

会被允许,这真的很奇怪。

相关问题