2013-10-22 38 views
1

例如,我必须确保某个实时系统的某个功能在20 ms或更短的时间内工作。我可以简单地在一个函数的开始和结束时测量时间,然后声明差异是令人满意的。我在C++中这样做。如何组织使用D契约的时间不变检查?

但是,这看起来很像合同,除了时间检查是一个后置条件,并且开始时的时间测量根本不是条件。把它放入合同不仅是为了它的标记,而且也是为了建立理由。

所以我想知道,我可以使用契约功能来检查功能工作的时间吗?

回答

2

排序,但不是很好。原因是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++一样。