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);
会?为何如此奇怪的语言设计决定?
这比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)
断言用于验证程序中某个特定点的程序状态.如果断言失败,则程序中存在错误,继续执行程序是没有意义的.但它确实需要运行一个断言 - 特别是涉及进行函数调用的一个 - 所以你通常在发布模式下禁用它们(-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)
要保证的代码路径在任何模式下都不会被命中.