1

例如,我必须确保某个实时系统的某个函数的工作时间为 20 毫秒或更短。我可以简单地在函数开始和结束时测量时间,然后断言差异是令人满意的。我在 C++ 中做到这一点。

但这看起来很像合同,只是时间检查是一个后置条件,而开始时的时间测量根本不是一个条件。将它纳入合同不仅是为了它的符号,而且也是为了建筑的原因。

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

4

1 回答 1

2

有点,但不是很好。原因是在 in{} 块中声明的变量在 out{} 块中不可见。(有一些关于改变这个的讨论,所以它可以通过在 in 块中复制来检查前后状态,但没有实现任何东西。)

因此,这将不起作用

void foo()
in { auto before = Clock.currTime(); }
out { assert(Clock.currTime - before < dur!"msecs"(20)); }
body { ... }

来自 in 的变量不会延续到 out,给您一个未定义的标识符错误。但是,我说“有点”,因为有一个潜在的解决方法:

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{} 块需要通过。因此,如果您在链中具有不同的 in{},它可能会忘记推送时间,然后当 out 尝试将其弹出时,您的堆栈会下溢。

// 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
  }
}

这似乎可行,但我认为这比它的价值更麻烦。我希望随着程序变大它会以某种方式中断,如果你的测试破坏了你的程序,那有点违背了目的。

如果仅使用显式测试进行检查就可以满足您的要求,我可能会将其作为 unittest{} 进行(但是,请注意,如果您使用 -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{} 块执行此操作,所以嗯,至少 before 变量是本地的。

底线,我会说你可能最好或多或少地以你在 C++ 中的方式去做。

于 2013-10-22T14:31:34.030 回答