D语言断言(假)

发布于 2024-11-28 09:26:45 字数 443 浏览 0 评论 0原文

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

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

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

显然,这样的构造在 D 中是不可能的。

更新

需要明确的是。问题是为什么int x=0; assert(x); 不会使程序的发布版本崩溃,但 assert(0); 会吗?为什么会有如此奇怪的语言设计决定?

TDPL describes a behavior of assert(false); statement. Such assertion is not removed from release build (as all other assertions) and, actually, stops the program immediately. The question is why? Why such confusing behavior? They might add halt(); or something like that to be able to stop the program.

Sometimes, I use the following construction in C++ code:

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

Obviously, such construction is not possible in D.

UPDATE

To be clear. The question is why int x=0; assert(x); won't crash a release version of a program, but assert(0); will? Why such strange language design decision?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

单调的奢华 2024-12-05 09:26:45

它比 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
}

that's older than D and it's generally used to tell the compiler you don't expect to ever get to that point in the code for because that would mean that something is very wrong in the code

typical use is like this

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
}
心碎的声音 2024-12-05 09:26:45

断言用于验证程序在程序中特定点的状态。如果断言失败,那么程序中就有bug,继续执行程序就没有意义了。但运行断言确实需要一些成本,尤其是涉及进行函数调用的断言,因此您通常在发布模式下禁用它们(dmd 的 -release 会自动执行此操作)。您运行程序并在调试模式下对其进行测试,希望您遇到导致断言失败的任何状态,以便您可以捕获并修复这些错误。然后你希望你已经抓住了它们,并且如果没有抓住的话,在发布模式下也不会发生什么可怕的事情。

但是,如果输入在任何情况下都不应该到达的代码路径呢?这就是 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 块来使编译器很高兴。然后,您将一个 assert(0) 放入 catch 块中,以便在它确实可以抛出时捕获它。

assert(0) 的经典案例实际上是由编译器本身使用的 - 即将它插入到不以 return 结尾的函数的末尾语句,这样如果代码的逻辑不正确并且您无论如何都会以某种方式结束于函数的末尾,则代码执行不会尝试继续。

在所有这些情况下,您所处理的代码路径只要您的代码正确就不可能命中,但它是一个安全网,以防您的逻辑错误。与裸暂停指令相比,使用 assert(0) 的优点是,当启用 -release 时,您可以获得正确的堆栈跟踪,并且可以得到一个不错的结果。带有它的错误消息。然后,当启用 -release 时,它会变成暂停指令,因此您保证您的程序不会陷入困境通过使用 assert(0) 越过该行来进入无效状态。

您可以对要在调试模式下验证但不在发布模式下验证的内容使用普通断言,并且对要保证的代码路径使用 assert(0)切勿在任何模式下点击。

An assertion is used to verify something about the state of your program at a particular point in the program. If the assertion fails, then there is a bug in the program, and it makes no sense to continue the execution of the program. But it does cost something to run an assertion - especially one that involves making function calls - so you usually disable them in release mode (-release for dmd does this automatically). You run your program and test it in debug mode and hopefully you hit any states that result in assertions failing so that you can catch and fix those bugs. Then you hope that you've caught them all and that nothing terribly bad happens in release mode if you didn't.

But what about entering code paths which should never be reached under any circumstances? That's where assert(0) comes in.

It functions like a normal assertion without -release, so you get nice stack traces and all that when you hit it, but because it's something that should not only never happen but would result in a completely invalid code path, it's left in in release mode but changed to a halt instruction. Classic places of where to use it would be cases such as

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

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

where the default case should never be hit, and if it is, the logic in your function is wrong, or

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

where as long as the logic of func is correct, it should be impossible for it to throw, but it called a function which can throw under some set of circumstances (just not these), so you have to use a try-catch block to make the compiler happy. You then put an assert(0) in the catch block so that you catch it if it really can throw.

The classic case for assert(0) is actually used by the compiler itself - and that is to insert it at the end of a function which doesn't end with a return statement so that the code execution does not attempt to continue if your code's logic is incorrect and you somehow end up at the end of the function anyway.

In all such cases, you're dealing with a code path which is impossible to hit as long as your code is correct, but it's a safety net in case you got your logic wrong. The advantages of using assert(0) for this over a naked halt instruction are that when -release is enabled, you get a proper stack trace, and you can have a nice error message with it. Then when -release is enabled, it gets turned into a halt instruction, so you're guaranteed that your program won't get itself into an invalid state by getting past the line with the assert(0).

You use normal assertions for stuff that you want to verify in debug mode but not in release mode, and you use assert(0) for code paths that you want to guarantee are never hit in any mode.

焚却相思 2024-12-05 09:26:45

您能够在发布版本中关闭断言的唯一原因是,当断言表达式可能即将通过时,不必测试断言表达式,从而节省时间。

“假”永远不会过去。

The only reason you are able to switch off asserts in release builds, is to save time by not having to test the assert expression when it's probably just going to pass.

"false" never passes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文