《Design by Contract for Embedded Software》 翻譯( 三 )


而這正是契約設計(DbC)理念的體現 。DbC 是由 Bertrand Meyer 開創的,他將軟件系統視為一組組件,這些組件的協作是基于精確定義的相互義務的規范--合同 1 。這樣做有兩個主要好處 。1)它自動幫助檢測錯誤(而不是 "處理 "它們),2)它是記錄代碼的最佳方式之一 。
You can implement the most important aspects of DbC (the contracts) in C or C++ with assertions. The Standard C Library macro assert() is rarely applicable to embedded systems, however, because its default behavior (when the integer expression passed to the macro evaluates to 0) is to print an error message and exit. Neither of these actions makes much sense for most embedded systems, which rarely have a screen to print to and cannot really exit either (at least not in the same sense that a desktop application can). Therefore, in an embedded environment, you usually have to define your own assertions that suit your tools and allow you to customize the error response. I’d suggest, however, that you think twice before you go about “enhancing” assertions, because a large part of their power derives from their relative simplicity.
你可以用斷言在 C 或 C++中實現 DbC 的最重要的方面(契約/合同) 。然而,標準 C 庫的宏 assert() 很少適用于嵌入式系統,因為它的默認行為(當傳遞給宏的整數表達式求值為 0 時)是打印一個錯誤信息并退出 。這兩種行為對大多數嵌入式系統來說都沒有什么意義 , 它們很少有屏幕可以打??,也不能真正晚樣zㄖ遼儼荒芟褡爛娉絳蚰茄順觶?。因此,在嵌入式環境中,你通常必須定義你自己的斷言,以適應你的工具并允許你自定義錯誤響應 。然而 , 我建議你在 "加強 "斷言之前三思而后行,因為斷言的很大一部分力量來自于其相對的簡單性 。
Listing 1. Embedded systems-friendly assertions
#ifndef qassert_h#define qassert_h/** NASSERT macro disables all contract validations * (assertions, preconditions, postconditions, and invariants). */#ifdef NASSERT /* NASSERT defined--DbC disabled */#define DEFINE_THIS_FILE#define ASSERT(ignore_)((void)0)#define ALLEGE(test_)((void)(test_))#else /* NASSERT not defined--DbC enabled */#ifdef __cplusplusextern "C"{#endif/* callback invoked in case of assertion failure */void onAssert__(char const *file, unsigned line);#ifdef __cplusplus}#endif#define DEFINE_THIS_FILE \static char const THIS_FILE__[] = __FILE__#define ASSERT(test_) \((test_) ? (void)0 : onAssert__(THIS_FILE__, __LINE__))#define ALLEGE(test_)ASSERT(test_)#endif /* NASSERT */#define REQUIRE(test_)ASSERT(test_)#define ENSURE(test_)ASSERT(test_)#define INVARIANT(test_) ASSERT(test_)#endif /* qassert_h */Listing 1 shows the simple embedded systems-friendly assertions that I’ve found adequate for a wide range of embedded projects. Listing 1 is similar to the standard <assert.h> (<cassert> in C++), except that the solution shown in Listing 1:
  • allows customizing the error response;
  • conserves memory by avoiding proliferation of multiple copies of the filename string;
  • provides additional macros for testing and documenting preconditions (REQUIRE), postconditions (ENSURE), and invariants (INVARIANT). (The names of the three last macros are a direct loan from Eiffel, the programming language that natively supports DbC.)
The all-purpose ASSERT() macro (lines 28-29 of Listing 1) is very similar to the standard assert(). If the argument passed to this macro evaluates to 0 (false), and if additionally the macro NASSERT is not defined, then ASSERT() will invoke a global callback onAssert__(). The function onAssert__() gives the clients the opportunity to customize the error response when the assertion fails. In embedded systems, onAssert__() typically first monopolizes the CPU (by disabling interrupts), then possibly attempts to put the system in a fail-safe mode, and eventually triggers a system reset. (Many embedded systems come out of reset in a fail-safe mode, so putting them in this mode before reset is often unnecessary.) If possible, the function should also leave a trail of bread crumbs from the cause, perhaps by storing the filename and line number in a nonvolatile memory. (The entry to onAssert__() is also an ideal place to set a breakpoint if you work with a debugger. TIP: Consult your debugger manual on how you can hard-code a permanent breakpoint in onAssert__().)
Compared to the standard assert(), the macro ASSERT() conserves memory (typically ROM) by passing THIS_FILE__ (Listing 1, line 26) as the first argument to 

推薦閱讀