When declaring an interface (as a class for example), there is a need to define the allowed calling order of functions. For example,
Init() must be called before any other function, and nothing should be called after
In the spirit of design by contract, I wrote this tiny class + macros to make such rules enforceable in runtime.
This code was tested on VC 6 and should work on any C++ compiler.
Design By Contract (DBC) is an issue that many people talk about but I did not see much practical work in the environment I work with (C++). DBC is all about clearly defining what your code should do (This might not be the best definition, but it will do for now).
While working on a complex project I found a bug caused by forgetting to call
Init() before the function I needed. Of course, it happened only in certain control flows, to make it more challenging.
The solution: Check in runtime that things happen as you expect them to be. Putting
ASSERT()s helps but it is not enough by itself.
Using the code
The code consists of one class and a few macros to make the source readable. For example, the statement
function B::foo() should be called after Init() and not after shutdown() is written as:
The full list of macros you can use (see also the .h file):
Enter(name) - call this as the first thing in your function, unless this is your
Called(name) - return
true if function
name was already called (anytime in the past).
FirstCall - return
true if no functions were called, including this function.
name was called just before the current function.
Require(boolean expression) will blow a fuse if expression evaluates to
false. Currently it is simply
To use the code, include DBC_EnforceOrder.h and in the class declaration add a macro:
In each function that may influence the state of "what is allowed to be called next", you have to add the
Enter(name) macro as in the
Finally, to turn it off, make sure the macro
_EnforceCallOrder is undefined. Currently it is defined in the H file.
- In every function (of interest) you must add
' Enter("function_name"). In another article I wrote, there is a way around it using symbol table engine - but this complicates the code enormously.
- I could add special rules such as
LAST_FUNCTION, and then check for this status in the
Enter() call. I chose not to do it for the tradeoff between simplicity and elegance.
- So far, only
REQUIRE (i.e. precondition) is implemented. It would be nice to add
ENSURE for post condition checking as well.
Enter() to have the full monti.
Points of interest
I wrote this code after failing to find in the Internet, the exact solution to what I needed. There are plenty of articles on DBC, but not so much frameworks to work with. And remember the KISS principle!
- 2003-09-25: First release