2013-10-22 25 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++一樣。