例如,我必须确保某个实时系统的某个功能在20 ms或更短的时间内工作。我可以简单地在一个函数的开始和结束时测量时间,然后声明差异是令人满意的。我在C++中这样做。如何组织使用D契约的时间不变检查?
但是,这看起来很像合同,除了时间检查是一个后置条件,并且开始时的时间测量根本不是条件。把它放入合同不仅是为了它的标记,而且也是为了建立理由。
所以我想知道,我可以使用契约功能来检查功能工作的时间吗?
例如,我必须确保某个实时系统的某个功能在20 ms或更短的时间内工作。我可以简单地在一个函数的开始和结束时测量时间,然后声明差异是令人满意的。我在C++中这样做。如何组织使用D契约的时间不变检查?
但是,这看起来很像合同,除了时间检查是一个后置条件,并且开始时的时间测量根本不是条件。把它放入合同不仅是为了它的标记,而且也是为了建立理由。
所以我想知道,我可以使用契约功能来检查功能工作的时间吗?
排序,但不是很好。原因是in {}块中声明的变量在out {}块中不可见。 (有过一些讨论要改变这一点,所以它可以在块进行复印在检查前VS后的状态,但什么也没有实现)。
所以,这将不工作:
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 {
}
}
声明该变量为结构的常规成员。但是这意味着每个函数都会有很多其他无用的变量,它们不适用于递归,并且会污染成员名称空间。
我的一部分认为你可以将自己的堆栈放在一边,并在{}推送时间,然后{}弹出它并检查....但快速测试显示它很容易一旦继承受到影响就会中断。如果您每次都在{}块中重复该操作,则可能会起作用。但这让我感觉非常脆弱。具有合同继承的规则是需要通过继承树的所有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中大多数断言那样的合约将被删除,所以我可能会将其作为unittest {}如果你需要它可靠地失败,抛出一个异常而不是断言,因为在调试和发布模式下,它总能工作。)。
或者你可以在函数或辅助结构体中使用assert或类似于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 */
}
当然,在这里你必须给它的子类太复制,但它似乎是你必须做的是与在{}块,无论如何,所以,以及至少前面的变量是本地的。底线,我想说你可能最好的做法或多或少像C++一样。