Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Programming with Contracts in C++


Programming with Contracts (PwC) is a method of developing software using contracts to explicitly state and test design requirements. The contract is used to define the obligations and benefits of program elements such as subroutines and classes. In this article, I explain contracts and present a technique for defining contracts in separate classes safely hidden away from implementation details.

Design by Contract (DbC) is often confused with PwC. Conceived by Bertrand Meyer, DbC is a formal software-design methodology, whereas PwC is the programming technique of using a contractual representation in code to verify that preconditions, postconditions, and invariants are satisfied. The differences can be equated to the relationship between object-oriented programming and object-oriented design. (For more information, http://www.artima.com/intv/contracts.html.)

Contracts

Contracts are made up of three major elements, referred to as "clauses": preconditions, postconditions, and class invariants. Preconditions and postconditions are clauses that are evaluated at the beginning and end of specific routines, respectively. From a design standpoint, a precondition represents the obligations on the context invoking the routine. An example can be found using the C file reading function fscanf:

int fscanf(FILE* stream,const char * format [ , argument , ...]);<br>

The function fscanf operates with the precondition that stream must be a pointer to an open file. There is also another precondition that there must be the same number of type specifiers in the format string as the number of arguments passed. Postconditions, however, represent both the obligations and benefits of a given subroutine. In the case of fscanf, the implicit postconditions are:

  • The return value is EOF if an error occurs; otherwise, it is the number of items successfully read not including any ignored fields.
  • The FILE* argument is updated.

From a theoretical standpoint a pre/ postcondition should be evaluated at compile time when appropriate. This can be done using BOOST_STATIC_ASSERT found in the Boost C++ library (http://boost/static_assert.hpp). Pre/postconditions conceptually are also language independent. In fact, some pre/postconditions are difficult and even impossible to express in a formal language. In these cases, the pre/postcondition is best expressed as a natural language comment.

A class invariant is a property of each instance of a class that is required to evaluate to True before and after every external call to a public function. One way to think of a class invariant is as a clause that is ANDed with the pre- and postcondition of each public method. Checking class invariants in C++ is beyond the scope of this article, but the same effect can be achieved more verbosely with pre/ postconditions.

Peppering Code with Assertions: Good, but Good Enough?

Most modern C++ programmers have adopted the good habit of (usually) checking their preconditions and postconditions in their code by placing assertions throughout their routines. This is fine for simple single-expression assertions, as the code generated can be completely removed by turning off assertion compilation.

Simply using assertions is somewhat unsatisfying as the set of preconditions and postconditions is buried deep in the code and is hard to extract. Automated tools can extract preconditions and postconditions, assuming they follow a consistent naming convention, but for a human perusing the code, the contracts of nontrivial functions and classes are not easily parsed.

The other drawback of assertions is that they only apply to code—if you have nontrivial clauses, then this approach starts to spill into noncontract code. Consider this example:

template<typename T><br>
class Stack {<br>
  void Push(T x) {<br>
    int nOldCount = Count()<br>
    // implementation here<br>
    assert(Count() == nOldCount + 1);<br>
  }<br>
 ...<br>
}<br>

Notice that the code for allocating space and initializing nOldCount will possibly remain in your executable, regardless of whether assertions are turned off.

Perhaps in this case, a compiler might optimize it away, but for nontrivial examples, it is virtually impossible for optimizers to remove all unreachable code. In this case, the simplest thing to do would be to wrap the nOldCount declaration in a #ifdef/#endif pair.

template<typename T><br>
class Stack {<br>
  void Push(T x) {<br>
    #ifdef CONTRACT_CHECKING_ON<br>
    int nOldCount = Count()<br>
    #endif // CONTRACT_CHECKING_ON<br>
    // implementation here<br>
    assert(Count() == nOldCount + 1);<br>
  }<br>
  ...<br>
}<br>

This may appear verbose, but the advantage that this code is effectively embedded with a test case that disappears entirely during release builds should be quite clear.

Writing code in this manner assures that the code is continually tested every time the code is executed. Retrofitting comparable test cases after code is already written is harder and almost invariably not as effective.

However, I have good news if you find this overly complex—there is a better way.

Using Contract Classes

The contract clearly needs to be separated from the implementation details. To separate it, you can define it in its own class. This makes the contract more easily parsed, and makes it reusable. The way I implement PwC in Heron—and which applies equally well to C++—is to define the contract as a template class that inherits from its primary type parameter, which you then use to wrap an implementation class. Consider this implementation class:

struct SortableIntArray_impl {<br>
 bool SortRange(int i, int j);<br>
 int GetAt(int i);<br>
 void SetAt(int i, int x);<br>
    };<br>

You can define the contract of the three public functions in code as in Listing One. Despite being conceptually simple, this contract required a significant amount of code to express. If this had been embedded directly in the implementation itself, it would become messy and confusing.

Listing One

template <typename T>
struct SortableIntArray_contract : public T {
  bool SortRange(int i, int j) {
    // preconditions
    assert(i >= 0);
    assert(i < size());
    assert(j >= i);
    assert(j < size());
    // implementation
    T::SortRange(i, j);
    // postconditions
    // in essence, is this array sorted
    for (int n=i; n < j; n++) {
      assert(get_at(n) <= get_at(n=1));
    }
  }
  int GetAt(int i) {
    // preconditions
    assert(i >= 0);
    assert(i <= size());
    return T::GetAt(i);
  }
  void SetAt(int  i, int x);
    // preconditions
    assert(i >= 0);
    assert(i <= size());
    T::SetAt(i, x);
    // postcondition
    assert(T::GetAt(i) == x);
  }
};

The contract can now be applied easily to the object conditionally as in Listing Two. The same effect can also be achieved using metatemplate programming techniques involving type selectors such as the STLSoft library compile-time function stlsoft::select_first_type or the Boost library compile-time function mpl::if_c.

Listing Two

#ifdef CONTRACT_CHECKING_ON
   typedef SortableIntArray_contract<SortableIntArray_impl> SortableIntArray;
#else
   typedef SortableIntArray_impl SortableIntArray;
#endif

Conclusion

Using assertions does allow the implementation of PwC, but using contract classes is better. Contract classes express and validate significantly complex contracts without obfuscating the implementation. Contract classes make design intentions explicit and improve the readability of code. Contract classes can also often be reused for classes with different implementations but with similar interfaces.

Acknowledgment

Thanks to Matthew Wilson for reviewing the article and pointing out that writing contracts that involve system conditions is a bad idea.

Download the source code that accompanies this article.


Christopher is a freelance computer programmer and developer of Heron, a modern, general-purpose, open-source language inspired by C++, Pascal, and Java. He can be contacted at http://www.heron-language.com/.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.