A nice article found through Reddit, "Design by Contract (DbC) for Embedded Software" by Miro Samek, 2009: http://www.netrino.com/Embedded-Systems/How-To/Design-by-Contract-for-Embedded-Software
It says nothing new, but it says such old things in a nice way, and I like how it compares assertions to fuses and how it contrasts DbC with a different kind of defensive programming that is kind of the opposite. A quotation from the article: >In contrast, every successful test run of code peppered with assertions builds >much more confidence in the software. I don't know exactly what the critical >density of assertions must be, but at some point the tests stop producing >undefined behavior, segmentation faults, or system hangs--all bugs manifest >themselves as assertion failures. This effect of DbC is truly amazing. The >integrity checks embodied in assertions prevent the code from "wandering >around" and even broken builds don't crash-and-burn but rather end up hitting >an assertion.< I have seen this with the DMD compiler itself, around half of its bugs are found by its assertions. Another quotation: >You can no longer design a system without accounting for testing overhead >right from the start. Assuming that all the CPU cycles, the RAM, and all the >ROM will be devoted strictly to the job at hand simply won't get the job done.< A third quotation, this seem different from D strategy (here the author is talking about normal PCs): >As an example, consider dynamic memory allocation. In any type of system, >memory allocation with malloc() (or the C++ new operator) can fail. In a >general-purpose computer, a failed malloc() merely indicates that, at this >instant the operating system cannot supply the requested memory. This can >happen easily in a highly dynamic, general-purpose computing environment. When >it happens, you have options to recover from the situation. One option might >be for the application to free up some memory that it allocated and then retry >the allocation. Another choice could be to prompt the user that the problem >exists and encourage them to exit other applications so that the current >application can gather more memory. Yet another option is to save data to the >disk and exit. Whatever the choice, handling this situation requires some >drastic actions, which are clearly off the mainstream behavior of your >application. Nevertheless, you should design and implement such actions >because in a ! desktop environment, a failed malloc() must be considered an exceptional condition.< Eventually (thanks to Sean too) D will have readable stack traces on all OSes, but to understand better what the program was doing when the assertion has fired you have to run the code in a debugger or to add lot of print statements (inside contracts!). As alternative, if the program keeps at runtime information about the types that are present in the all the stack frames (this can be done with LLVM, there is support for precise stack scanning, for certain kinds of GCs), then when the stack trace gets written it can also optionally print the state of all the variables in all the stack frames. This logged text can later help debug the program even if the program was running on the production machine (with stack tracing activated and precise stack types activated). This is a paper that shows why DbC can not enough in some situations, by Ken Garlington, 1998: http://home.flash.net/~kennieg/ariane.html In the situation like the one of the Ariane I think the good solution is the introduce a fuzzy control system that has a degradation of its effectiveness as conditions come out of its specs, but avoids a total failure. This is what biological designs too do. It's a kind of 'defensive programming'. Bye, bearophile