用D语言断言(假)

时间:2011-08-06 16:55:32

标签: assert d

TDPL描述了assert(false);语句的行为。这样的断言不会从发布版本中删除(与所有其他断言一样),并且实际上会立即停止程序。问题是为什么?为什么这么混乱的行为呢?他们可能会添加halt();或类似的东西以便能够停止该程序。

有时,我在C ++代码中使用以下结构:

if (something_goes_wrong)
{
   assert(false);
   return false;
}

显然,这种结构在D中是不可能的。

更新

要清楚。问题是为什么int x=0; assert(x);不会崩溃程序的发布版本,但assert(0);会?为什么这么奇怪的语言设计决定?

3 个答案:

答案 0 :(得分:8)

它比D更早,它通常用于告诉编译器你不希望在代码中达到那一点,因为这意味着代码中的某些东西是非常错误的

典型用法就是这样

MyStruct foo(){
    foreach(s;set){
        if(someConditionGuaranteedToHoldForAtLeastOne(s))
            return s;
    }
    //now what I can't return null;
    assert(0);//tell the compiler I don't expect to ever come here
}

答案 1 :(得分:7)

断言用于验证程序中某个特定点的程序状态。如果断言失败,则程序中存在错误,继续执行程序是没有意义的。但是 花费了一些东西来运行一个断言 - 特别是涉及进行函数调用的东西 - 所以你通常在发布模式下禁用它们(-release为dmd自动执行此操作)。您运行程序并在调试模式下测试它,并希望您点击导致断言失败的任何状态,以便您可以捕获并修复这些错误。那么你希望你已经抓住了所有这些,并且如果你没有,那么在发布模式中没有发生任何非常糟糕的事情。

但是在任何环境下输入应该永远不会的代码路径呢?这就是assert(0)的用武之地。

它的功能类似于没有-release的正常断言,所以当你点击它时你会得到很好的堆栈跟踪,但是因为它不仅不会发生,而且会导致完全无效的代码路径,它在释放模式下保留,但更改为暂停指令。在哪里使用它的经典场所将是

等案例
switch(var)
{
    ...
    //various case statements that should cover all possible values of var
    ...

    default:
        assert(0, format("Invalid value for var: %s", var));
}

其中永远不应该触发默认情况,如果是,则函数中的逻辑是错误的,或者

string func(int i) nothrow
{
    try
        return format("%s", i);
    catch(Exception e)
        assert(0, "Format threw. That should be impossible in this case.");
}

只要func的逻辑是正确的,它就不可能抛出,但是它调用了一个可以在某些情况下抛出的函数(只是没有)这些),所以你必须使用try-catch块来使编译器满意。然后在catch块中放置一个assert(0),以便在它真正可以抛出时捕获它。

assert(0)经典案例实际上是由编译器本身使用的 - 也就是将它插入到一个不以return语句结尾的函数的末尾,以便如果代码的逻辑不正确,并且无论如何最终都会在函数结束时代码执行不会继续尝试。

在所有这些情况下,只要你的代码是正确的,你就会处理一个无法命中的代码路径,但如果你的逻辑错误,它就是一个安全网。在裸停止指令上使用assert(0)的优点是,当启用-release时,您将获得正确的堆栈跟踪,并且您可以使用它获得一个很好的错误消息。然后,当启用-release 时,它将变为暂停指令,因此您保证您的程序不会通过以下方式进入无效状态:超越assert(0)

您对要在调试模式下验证但未在发布模式下验证的内容使用正常断言,并且使用assert(0)代码路径,您希望保证永远不会在任何模式。

答案 2 :(得分:6)

你能够在发布版本中关闭断言的唯一原因是,当它可能刚刚通过时,不必测试断言表达式来节省时间。

“false”永远不会过去。