用D语言断言(假)

Sta*_*tas 6 assert d

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

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

if (something_goes_wrong)
{
   assert(false);
   return false;
}
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

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

rat*_*eak 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
}
Run Code Online (Sandbox Code Playgroud)

  • 这个.像这样的问题通常来自误解断言目的的人.失败的断言意味着某些事情已经发生了可怕的错误,并且发生了一些永远不会发生的事情,除非您的代码中存在错误. (5认同)
  • @Stas:关于立即崩溃程序:IIRC设计断言背后的想法不允许这样做.断言是指程序非常严重的情况,你可以安全地做的唯一事情是尽快停止.在应该使用断言的情况下,你甚至无法安全地清理外部状态,因为"不可能"已经发生,你要清理的东西可能无法修复. (2认同)

Jon*_*vis 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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

只要逻辑func是正确的,它就不可能抛出,但是它调用了一个可以在某些情况下抛出的函数(只是不是这些),所以你必须使用try-catch块来制作编译器开心.然后你把一个assert(0)放在catch块中,这样你可以抓住它,如果真的可以抛出它.

经典案例assert(0)实际上是由编译器使用本身-这是在不与return语句结束函数的末尾插入它,以便执行代码不会尝试继续,如果你的代码的逻辑是不正确的,无论如何,你最终会在函数的最后结束.

在所有这些情况下,只要你的代码是正确的,你就会处理一个无法命中的代码路径,但如果你的逻辑错误,它就是一个安全网.使用assert(0)裸露暂停指令的优点是,当-release启用时,您将获得正确的堆栈跟踪,并且您可以使用它获得一个很好的错误消息.然后,当-release 激活,它就会变成一个暂停指令,所以你保证,你的程序将不会通过让过去与行获得自己变成无效状态assert(0).

对于要在调试模式下验证但未在发布模式下验证的内容,可以使用常规断言,并且您可以使用assert(0)保证的代码路径在任何模式下都不会被命中.


Fee*_*ure 6

您可以在发布版本中关闭断言的唯一原因是,在可能只是通过时不必测试断言表达式来节省时间.

"假"永远不会过去.