2014-02-23 25 views
1

我想实现此行为:如何使健壮的断言?

  • 当程序在调试模式下运行,assert_robust(expression, commands)工作严格喜欢古典assert(expression)
  • 当程序在发布模式下运行,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++的方式)

而另一个问题是:

是它是在调试严格和善意的释放好事吗?

编辑:我的想法,为什么做这样的事情是因为,如果它是在程序,该程序有时比保持崩溃时和用户丢失其数据有点怪异的错误practicaly好得多。

+2

这听起来很像尝试使用assert进行错误处理...(这不是一个好主意TM) – Xarn

+0

@ Xarn:你能解释为什么它不是个好主意吗? – user3123061

+2

断言通常意味着程序员可以使用它来检查自己的工作,其中错误处理(返回码,异常,errno)旨在提供对公共API的检查。 (断言基本上证明了你认为的逻辑上不可能的执行状态,因此你不应该将它们绊倒。例外情况说明别人搞砸了。) – Xarn

回答

3

更有趣的问题是第二:

它是好东西是在调试严格和善意的释放?

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

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

+1

“如果您可以在发布模式下安全恢复,为什么在DEBUG中声明? 也许你是对的。我完全不知道,我有直觉,我想做它的好事。 :-)但也有一个有趣的比喻: 某些文件格式的作者严格遵守该格式的规范。但读者更仁慈,容错 - 这很好。 – user3123061

+0

@ user3123061:但是读者可以从错误中恢复,或者它不能。如果您认为可以恢复,则应尝试在调试和发布模式下恢复,因为这是您可以明确测试恢复的唯一方式。你在这里说的是你想调试一个应用程序,然后在生产中运行一个类似但不同的应用程序。 –

+0

我不认为我可以在这里恢复。如果有东西给我负指数,我不知道为什么。那种错误肯定不会发生。但是,如果发生,然后在调试我想要停止编程,我看着堆栈跟踪找到bug的来源。但是在发行版中,我不希望用户付税,分心了一些错误列表和崩溃,为我的调试生活更轻松。我的程序中的错误是我的问题,没有用户问题。我而不是用户必须努力寻找什么问题。帮助我调试程序根本不是用户的工作。 – user3123061

0

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

但是如果你想中止之前运行某种紧急命令,你可以使用从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); 
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 */ 

将被允许,这将是非常奇怪的。