23. Appendix A — Design by Contract Details

To quote from Wikipedia:

“The central idea of DbC is a metaphor on how elements of a software system collaborate with each other on the basis of mutual obligations and benefits. The metaphor comes from business life, where a “client” and a “supplier” agree on a “contract” that defines for example that:

  • The supplier must provide a certain product (obligation) and is entitled to expect that the client has paid its fee (benefit).

  • The client must pay the fee (obligation) and is entitled to get the product (benefit).

  • Both parties must satisfy certain obligations, such as laws and regulations, applying to all contracts.

“Similarly, if a routine from a class in object-oriented programming provides a certain functionality, it may:

  • Expect a certain condition to be guaranteed on entry by any client module that calls it: the routine’s “precondition” — an obligation for the client, and a benefit for the supplier (the routine itself), as it frees it from having to handle cases outside of the precondition.

  • Guarantee a certain property on exit: the routine’s “postcondition” — an obligation for the supplier, and obviously a benefit (the main benefit of calling the routine) for the client.

  • Maintain a certain property, assumed on entry and guaranteed on exit: the class invariant. (An “class invariant” is a condition that must remain true whenever the object is in a stable state [i.e. not in the middle of a multi-step operation].)

“The contract is semantically equivalent to a Hoare triple [in the context of “software correctness proofs” — see https://en.wikipedia.org/wiki/Hoare_logic] which formalises the obligations. This can be summarised by the “three questions” that the designer must repeatedly answer in the contract:

  • What does contract expect?

  • What does contract guarantee?

  • What does contract maintain?

“Many programming languages have facilities to make assertions like these. However, DbC considers these contracts to be so crucial to software correctness that they should be part of the design process. In effect, DbC advocates writing the assertions first. Contracts can be written by code comments, enforced by a test suite, or both, even if there is no special language support for contracts.”

In our development tasks in C, Design by Contract contracts are implemented via assertions that detect when the contract is broken (an assertion fails). When this happens, something (decided by the developer) can be triggered as a result. The usual “something” that is triggered is a software breakpoint causes the debugger to halt, and a message is made available that spells out both the nature of the problem, and the halted debugger gives its location within the source code. This effectively detects and isolates bugs before they make it into production code. It is typically a joy when it happens, because typically hours, days or weeks have just been saved by having detected sometimes very hard-to-find bugs!

It is the case that there are many contexts in which certain routines must only be called under certain circumstances. Such circumstances are typical in virtually all software development, particularly where the code, and conditions under which it is executed, are complex. Such situations abound in firmware development where external chips must be operated in a certain exact way or else they will not function properly (where correct conditions often involve time limitations, signal states, and complex chip states). It stands that in virtually any complex software development efforts, implementation of Design by Contract often means significantly shorter development efforts with resulting higher quality and more reliable software.

DbC also often results in faster software as well: without Contracts, programmers are pressured to write “defensive code”. Such code is often seen in checks for input argument value ranges and returning error codes when input arguments are not valid. Often these are cases where such things can AND SHOULD be being done by the caller — not the routine being called. When this is the case, DbC encourages developers to instead write a Contract Assertion. Contract Assertions declare with zero ambiguity, and enforce, where the responsibility lies (client or supplier) for certain conditions to be met. Preconditions declare the conditions that the client (caller) is responsible for. Postconditions declare the conditions that the supplier is responsible for. And Checks (assertions anywhere in code) validate assumptions that the developer wants to be certain of, and/or alerted about if they are ever proven false.

Writing a Contract Assertion instead of “defensive code” is especially valid in a closed system where the callers of a routine can be guaranteed to all be passing correct values under all circumstances (as opposed to a library). Once testing is completed and the software is finalized, “defensive code” would now only needlessly consume CPU overhead and in such cases as a closed system, no longer serves a useful purpose.

Design by Contract solves this problem by permitting such contracts to be included or excluded in “degrees” depending on the stage of development, and continuing needs. To what degree this is done is entirely in the hands of the developer. A typical scheme is that all Contract Assertions are “turned on” during development and testing, and then when the __DEBUG macro is no longer defined (for code nearing final testing, and for production code), these checks (and the CPU overhead they consume) are entirely removed from the system. Other schemes involve turning off some of the assertions and leaving others on (typically preconditions and checks — see definitions above) as a part of error logging or otherwise detecting and communicating internal system problems). Again, how much of these checks are left on in the program is entirely up to the developer.

For libraries, precondition Contract Assertions are particularly valuable, all for the same reasons.

In our C development environment, these assertions are implemented by preprocessor definitions that turn preconditions (that check client obligations), postconditions (that check supplier obligations), and straight assertions (that check assumptions anywhere in code) into function calls that causes the desired triggered event. In most cases (as stated above), this event is that the debugger stops at the place of the problem, and quickly gives the programmer a clear understanding of the problem. The syntax for these assertions is:

require(<message>, <condition>);  /* precondition */

ensure(<message>, <condition>);   /* postcondition */

check(<message>, <condition>);    /* check / assumption verification */

where:

<message>:

is a sequence of characters (no quotation marks) without spaces, using underscores between words, expressing in the developer’s language, the condition that should be met (the state of things when <condition> is TRUE), and

<condition>:

is a boolean expression that tests the condition and must return TRUE for the condition to “pass” the test.

Examples:

require(nand_selected, mNAND_S34MLxxG1xx__IS_SELECTED());
require(cle_low, mNAND_S34MLxxG1xx__CMD_LATCH_IS_DISABLED());
require(ale_high, mNAND_S34MLxxG1xx__ADDR_LATCH_IS_ENABLED());

The variables and/or functions used in the boolean condition, in principle, should be accessible to the caller. This is especially important for libraries, since the caller may need to test the call-validity condition before making the call!

23.1. Commands and Queries

In the C language, all subroutines (Assembly Language vocabulary) are called “functions”. However, there are different schools of thought about the use of the term “function”, since it has particular meaning in the Mathematics realm (an operation that returns a value). Some schools of thought clarify this discrepancy by dividing subroutines up into two categories for clarity when the distinction is important: “functions” (which return a value), and “procedures” (which return no value, or void).

As a part of implementing Design by Contract, we take this clarification one step further by using the vocabulary of “Design by Contract” and name these, instead:

  • commands = procedures, always return void.

  • queries = functions, always return a type other than void.

  • routine = either a command or a query when the distinction is not important.

And in the C language context, we also use:

  • function = either a command or a query when the distinction is not important.

The distinction between commands and queries is important because Design by Contract implements a self-checking mechanism that validates (among other things), the “contract” (or agreement) between client and supplier.

In doing this, it may execute any number of queries (functions) to validate the conditions being checked. And because of this, we implement the following:

Policy:

Commands can alter the objects they operate on. Queries are not allowed to alter the objects they operate on.

This enables the method to guarantee that queries (functions) used in Contract Assertions cannot cause side effects. This is very important when such assertions can be executed multiple times during debugging, but may execute a different number of times (or not at all) when some or all of them are automatically removed (require, ensure, and check assertions can be selectively turned on and off) or when __DEBUG is not defined in the compilation.

23.1.1. Note on Command/Query Separation

Maintaining strict Command/Query separation is not always possible, especially when coding for legacy OS operations such as file system operations, which may have certain legacy requirements already in place that violate this separation rule. (While it is certainly possible to implement OS operations in a way that does maintain such separation, by providing a query that provides the success status or results of a command, it recognized that this is not always possible, especially if the developer does not control the libraries he may be working with. However it remains imperative where Design-by-Contract is in use that:

all queries used in Contract Assertions *never* cause side effects.