例如,我必须确保某个实时系统的某个功能工作20毫秒或更短时间。我可以简单地在函数的开头和结尾处测量时间,然后断言差异是令人满意的。我用C ++做到这一点。
但这看起来非常像契约,除了时间检查是一个后置条件,开始时的时间测量根本不是条件。将它签订合同不仅仅是为了它的符号,而且也是为了建立理由。
所以我想知道,我可以使用合同功能来检查功能工作的时间吗?
答案 0 :(得分:2)
排序,但不是很好。原因是在{}块中声明的变量在out {}块中不可见。 (有一些关于改变它的讨论,所以它可以通过在块中复制来检查pre和post状态,但是没有实现。)
所以,这将不工作:
void foo()
in { auto before = Clock.currTime(); }
out { assert(Clock.currTime - before < dur!"msecs"(20)); }
body { ... }
来自in的变量不会结转出来,给你一个未定义的标识符错误。但是,我说“有点”,因为有一个潜在的解决方法:
import std.datetime;
struct Foo {
SysTime test_before;
void test()
in {
test_before = Clock.currTime();
}
out {
assert(Clock.currTime - test_before < dur!"msecs"(20));
}
body {
}
}
将变量声明为结构的常规成员。但是这意味着每个函数都有很多无用的变量,不能用于递归,只会污染成员命名空间。
我的一部分人认为你可以将你自己的筹码放在一边并且在{}推动时间,然后在{}弹出并检查......但是快速测试表明它可能会破坏一旦继承涉及。如果每次重复in {}块,它可能会起作用。但这让我觉得非常脆弱。具有契约继承的规则是继承树的所有out {}块都需要传递,但只有任何一个in}}块需要传递。因此,如果链中有一个不同的{},它可能会忘记推动时间,然后当试图弹出它时,你的堆栈会下溢。
// just for experimenting.....
SysTime[] timeStack; // WARNING: use a real stack here in production, a plain array will waste a *lot* of time reallocating as you push and pop on to it
class Foo {
void test()
in {
timeStack ~= Clock.currTime();
}
out {
auto start = timeStack[$-1];
timeStack = timeStack[0 .. $-1];
assert(Clock.currTime - start < dur!"msecs"(20));
import std.stdio;
// making sure the stack length is still sane
writeln("stack length ", timeStack.length);
}
body { }
}
class Bar : Foo {
override void test()
in {
// had to repeat the in block on the child class for this to work at all
timeStack ~= Clock.currTime();
}
body {
import core.thread;
Thread.sleep(10.msecs); // bump that up to force a failure, ensuring the test is actually run
}
}
这似乎有效,但我认为这比它的价值更麻烦。我希望随着程序变大,它会以某种方式破坏,如果你的测试破坏了你的程序,那就有点失败了。
如果仅使用显式测试检查满足您的要求,我可能会将其作为单元测试{},但请注意,如果使用-release开关进行编译,合同(如D中的大多数断言)将被删除,因此它们是实际上也不会在发行版本中进行检查。如果你需要它可靠地失败,抛出一个异常而不是断言,因为在调试和发布模式下它总能工作。)。
或者您可以使用函数中的断言或辅助结构或其他任何东西,类似于C ++。我会使用范围守卫:
void test() {
auto before = Clock.currTime();
scope(exit) assert(Clock.currTime - before < dur!"msecs"(20)); // or import std.exception; and use enforce instead of assert if you want it in release builds too
/* write the rest of your function */
}
当然,在这里你也必须在子类中复制它,但似乎你必须使用in {}块来做到这一点,所以meh,至少before变量是本地的。
最重要的是,我认为你最好不要像在C ++中那样做得更好。