Posted on April 2nd, 2006 at 22:41 by fr3@K
Introduction
assert() 是個用來 debug 的 macro, 接受一個 expression. 如果 expression 被 evalute 的結果為整數零, 或能被 convert 成零 (如 null pointer), 則會引發 assertion failure. Assertion failure 會 output 被 evluate 為零的 expression 以及其位置, 包括 file name, line number (有些 compiler 也會 output function name) 至 standard error. 緊接著, 發生 assertion failure 的 process 會呼叫 abort(), 導致 process 結束 (有些 compiler會產生 core dump). 用以驗證下列 precondition:
- Client code 對 function 的操作, 例如給予的參數合不合規範, 或呼叫相關 functions 的順序是否正確
- Function 作者的假設
Assertion Failure Output
假設有個在 str_util.c 的 library function:
size_t string_length (const char* str)
{
// Precondition assertion, client input buffer
// must be a valid string and must not be null!
assert(str != NULL);
// ...
}
當 assertion failure 發生時, user 會於 standard error (通常就是 terminal) 看到類似這樣的訊息:
AppName: str_util.c:245: size_t string_length():
Assertion `str != NULL' failed.
這訊息對 user (programmer) 來說通常沒有多大助益, 除非 user 剛好熟悉這段發生 assertion failure 的 code, 或是能取得發生 assertion failure 的 source code.
假設 user 能取得這段 source code, 有時就算沒有作者的 comment, 也應該能合理堆測作者對這 function 設下了precondition, 此例的 precondition 為 client code 所給用以讀取的 buffer (str) 不能為 NULL (當然, 所有 precondition 最好是寫在說明文件, 若沒文件最起碼也要有 comment).
如果你是這 library function 的作者, 除了做好說明文件以及在 source code 裡寫足 comment, 還有什麼是你能為使用者做的? 讓這使用者在發生 assertion failure 時更容易修正 client code.
More Verbose Output
Imperfect C++ (希望沒記錯) 提出了一個小技巧, 用在這個地方特別適用. 把上例做點小小修改:
size_t string_length (const char* str)
{
assert("Precondition failed, client supplied "
"input buffer must be a valid string and must "
"not be null" && str != NULL);
// ...
}
幾乎只是把原來的 comment 搬到 assert 的 expression 中, 就能在 assertion failure 發生時產生更有用的 output:
AppName: str_util.c:245: size_t string_length():
Assertion `"Precondition failed, client
supplied input buffer must be a valid string
and must not be null" && str != NULL' failed.
這樣的訊息對 user 來說明顯的有用多了, user 可以清楚知道 client code 給了 string_length() 一個 null pointer, 而這違反了該 function 的 precondition. User 不需要取得該 function 的 source code (甚至不用參考說明文件) 也能知道如何修正這個錯誤.
Macro Side-Effect
assert 是個 macro, 即使是經驗老到的人, 有時也會忽略掉若是以 -DNDEBUG 或是 M$VC 所謂的 Relase Mode 編譯, assert 不但不會產生任何效果, 給 assert 的 expression 更是不會被 evaluate. 下面的 code, 以 -DNDEBUG 編譯, foo() 不會被呼叫.:
int foo ();
void bar ()
{
assert(foo());
}
Macro Side-Effect, C++ Specific
C++ Standard Library 繼承了絕大部份 (也許是全部, 但我不確定) C Standard Library. 只是在 include standard header 時, 由原來的:
#include
變成:
#include
而且會把在該 header 中屬於 global namespace 的 declartion, 移到 std 這個 namespace 當中. 但由於 macro (errno 也是 macro) 是 preprocessor 直接替換的, 與 namespace 無關 (compiler 才懂 namespace). 因此使用起來像是存在於所有 namespace 當中 (這也是盡量別用 macro 的原因之一).
Scenarios Not to Use Assert
有些情況, 寫 library 的人會錯誤地使用 assert, 如:
class SomeClass;
void faux (SomeClass* ptr)
{
assert(ptr != NULL);
// ...
}
void foobar (int n)
{
assert(n >= 0);
// ...
}
如果你的 code 看起來像上例的話, 請像下面這樣, 把 interface 改一改吧:
void faux (SomeClass& instance)
{
// ...
}
void foobar (unsigned int n)
{
// ...
}
![]() |
|
| Previous Post « Feeding Frenzy « |
Next Post » Free Software vs Open Source » |







