《Design by Contract for Embedded Software》 翻译( 六 )

This implementation is intentionally less flexible, but unlike the defensive version, it’s hard to use this one incorrectly. Additionally (although difficult to convincingly demonstrate with a toy example like this), assertions tend to eliminate a lot of code that you would have to invent to handle the wider range of inputs allowed in the defensive code.

这个实现故意不那么灵活,但与防御性版本不同,很难错误地使用这个版本 。此外(虽然很难用这样的玩具例子来令人信服地证明) , 断言倾向于消除很多你必须发明的代码,以处理防御性代码中允许的更大范围的输入 。
But there is more, much more to DbC than just complementing defensive programming. The key to unveiling DbC’s full potential is to preemptively look for conditions to assert. The most effective assertions are discovered by asking two simple questions: “What are the implicit assumptions for this code and the client code to execute correctly?” and “How can I explicitly and most effectively test these assumptions?” By asking these questions for every piece of code you write, you’ll discover valuable conditions that you wouldn’t test otherwise. This way of thinking about assertions leads to a paradigm shift from “defensive” to “preemptive” programming, in which you preemptively look for situations that have even a potential of breeding bugs.
但是,DbC 不仅仅是补充防御性编程,还有更多,更多 。揭示 DbC 全部潜力的关键是先发制人地寻找断言的条件 。最有效的断言是通过问两个简单的问题发现的 。"这段代码和客户代码正确执行的隐含假设是什么?"和 "我怎样才能明确和最有效地测试这些假设?" 通过对你写的每一段代码提出这些问题,你会发现有价值的条件,否则你就不会去测试 。这种对断言的思考方式导致了从 "防御性 "到 "先发制人 "的编程模式的转变,在这种模式下,你会先发制人地寻找那些甚至有可能滋生错误的情况 。
To this end, embedded systems are particularly suitable for implementing such a “preemptive doctrine.” Embedded CPUs are surrounded by specialized peripherals that just beg to be used for validating correct program execution. For example, a serial communication channel (say, a 16550-type UART) might set a bit in the Line Status Register when the input FIFO overflows. A typical serial driver ignores this bit (after all, the driver cannot recover bytes that already have been lost), but your driver can assert that the FIFO overrun bit is never set. By doing so, you’ll know when your hardware has lost bytes (perhaps due to an intermittent delay in servicing the UART), which is otherwise almost impossible to detect or reproduce at will. Needless to say, with this information you’ll not waste your time on debugging the protocol stack, but instead you’ll concentrate on finding and fixing a timing glitch. You can use timer/counters in a similar way to build real-time assertions that check if you miss your deadlines. In my company, we’ve used a special register of a GPS correlator chip to assert that every GPS channel is always serviced within its C/A code epoch (around every 1 ms)—yet another form of a real-time assertion. The main point of these examples is that the information available almost for free from the hardware wouldn’t be used if not for the “preemptive” assertions. Yet, the information is invaluable, because it’s often the only way to directly validate the time-domain performance of your code.
为此,嵌入式系统特别适合实施这种 "先发制人理论" 。嵌入式 CPU 周围有专门的外设 , 这些外设正好可以用来验证程序的正确执行 。例如,当输入 FIFO 溢出时,一个串行通信通道(例如 16550 型 UART)可能在线路状态寄存器中设置一个位 。一个典型的串行驱动程序会忽略这个位(毕竟,驱动程序不能恢复已经丢失的字节),但是你的驱动程序可以断言 FIFO 溢出位从未被设置 。通过这样做,你就可以知道你的硬件何时丢失了字节(也许是由于 UART 中断服务的间歇性延迟),否则几乎不可能随意检测或再现 。不用说 , 有了这些信息,你就不会把时间浪费在调试协议栈上,而是集中精力寻找和修复一个定时故障 。你可以以类似的方式使用定时器/计数器来建立实时断言,检查你是否错过了最后期限 。在我的公司里 , 我们使用 GPS 相关芯片的一个特殊寄存器来断言每个 GPS 通道总是在其 C/A 代码纪元内得到服务(大约每 1ms)--这是实时断言的另一种形式 。这些例子的主要观点是,如果不是 "先发制人 "的断言,几乎可以从硬件中免费获得的信息不会被使用 。然而,这些信息是无价的,因为它往往是直接验证你的代码的时域性能的唯一方法 。

推荐阅读