2014-02-23 62 views
1

我想實現此行爲:如何使健壯的斷言?

  • 當程序在調試模式下運行,assert_robust(expression, commands)工作嚴格喜歡古典assert(expression)
  • 當程序在發佈模式下運行,assert_robust(expression, commands)執行一些commands如果表達式爲假

這是可以做到這樣:

#ifdef NDEBUG 
    #define assert_robust(expression, command) if (!(expression)) command; 
#else 
    #define assert_robust(expression, command) assert(expression); 
#endif 

這可以用於例如這樣做myfunction容錯:

char myfunction(const string& s, int i) 
{ 
    assert_robust(i >= 0, return '\0'); 

    /* Normal code */ 
} 

這項工作做得很好,但如何讓支持的命令不止一個(任意)號宏assert_robust? (最好用標準C++的方式)

而另一個問題是:

是它是在調試嚴格和善意的釋放好事嗎?

編輯:我的想法,爲什麼做這樣的事情是因爲,如果它是在程序,該程序有時比保持崩潰時和用戶丟失其數據有點怪異的錯誤practicaly好得多。

+2

這聽起來很像嘗試使用assert進行錯誤處理...(這不是一個好主意TM) – Xarn

+0

@ Xarn:你能解釋爲什麼它不是個好主意嗎? – user3123061

+2

斷言通常意味着程序員可以使用它來檢查自己的工作,其中錯誤處理(返回碼,異常,errno)旨在提供對公共API的檢查。 (斷言基本上證明了你認爲的邏輯上不可能的執行狀態,因此你不應該將它們絆倒。例外情況說明別人搞砸了。) – Xarn

回答

3

更有趣的問題是第二:

它是好東西是在調試嚴格和善意的釋放?

我的經驗是,在調試和發佈版本中有不同的行爲是一個可怕的想法。您正在註冊生產中的問題,因爲兩者的行爲不同,您將無法在調試版本中重現問題。

除此之外,如果您在調試模式中首先聲明不會成爲問題,則應使用斷言來標記編程問題,您無法安全恢復的情況。如果您可以在發佈模式下安全恢復,爲什麼在DEBUG中聲明?如果你不能,你是否願意用一種你不太明白它會做什麼的方式來操縱生產數據?

+1

「如果您可以在發佈模式下安全恢復,爲什麼在DEBUG中聲明? 也許你是對的。我完全不知道,我有直覺,我想做它的好事。 :-)但也有一個有趣的比喻: 某些文件格式的作者嚴格遵守該格式的規範。但讀者更仁慈,容錯 - 這很好。 – user3123061

+0

@ user3123061:但是讀者可以從錯誤中恢復,或者它不能。如果您認爲可以恢復,則應嘗試在調試和發佈模式下恢復,因爲這是您可以明確測試恢復的唯一方式。你在這裏說的是你想調試一個應用程序,然後在生產中運行一個類似但不同的應用程序。 –

+0

我不認爲我可以在這裏恢復。如果有東西給我負指數,我不知道爲什麼。那種錯誤肯定不會發生。但是,如果發生,然後在調試我想要停止編程,我看着堆棧跟蹤找到bug的來源。但是在發行版中,我不希望用戶付稅,分心了一些錯誤列表和崩潰,爲我的調試生活更輕鬆。我的程序中的錯誤是我的問題,沒有用戶問題。我而不是用戶必須努力尋找什麼問題。幫助我調試程序根本不是用戶的工作。 – user3123061

0

我不認爲這種方式使用斷言是一個好主意。通常,如果您希望謂詞始終爲真,那麼通常使用斷言,因爲它是關鍵代碼的一部分。如果不是真的,那顯然是一個很大的問題,並且放棄是合理的。但越來越多的人使用assert錯誤檢查進行調試。在這種情況下,它足以在發佈模式下完全禁用它。它認爲你應該決定這兩種方法之一。

但是如果你想中止之前運行某種緊急命令,你可以使用從C++ 11的新的lambda函數:

void ASSERT(int expression, std::function<void()> func) { 
    if(!expression) { 
     if (func) func(); 
     abort(); 
    } 
} 

你可以使用這樣的:

ASSERT(a >= 0, []() { std::cerr << "ERROR" << std::endl;}); 

或者:

ASSERT(a >= 0, [this]() { this->terminate(); }); 

或者:

ASSERT(a >= 0, nullptr); 
0

不考慮這個問題,如果這是一個好主意,您可以使用宏在do-while(0);循環中包裝多個命令。

#ifdef NDEBUG 
    #define assert_robust(expression, command) if (!(expression)) \ 
               do{command;} while(0) 
#else 
    #define assert_robust(expression, command) assert(expression) 
#endif 

還要注意,我在宏的末尾沒有包含分號。如果你將它們包含在宏中,那麼類似於

assert_robust(cond1, command1) /* no semicolon here, no problem */ 
assert_robust(cond2, command2) /* no semicolon here, no problem */ 

將被允許,這將是非常奇怪的。