C++: Implementing Design by Contract to reduce bugs
Takeaway: Design by Contract is a software paradigm that can ensure your code will have fewer bugs. See how it can improve your code in this overview.
Design by Contract is a software paradigm that, when used, ensures that your code will have fewer bugs. It does so by enforcing "contracts." A contract is made by a function based on what it expects when you enter it (preconditions) and what it yields when it exits (postconditions). For example, an int factorial(int n) function expects n to be positive (precondition), and it will yield a positive value (postcondition). Use the Design by Contract paradigm, and your code will contain fewer bugs—a lot of programmers believe it, and their experience proves it.
How it works
A function employs Design by Contract (DbyC) if it has preconditions and postconditions—conditions that should always be met—and it tests that those conditions indeed exist.
You can employ DbyC in C++ by:
- Using the assert function (or some similar macro)
- Throwing an exception in case a precondition or postcondition fails
- Using assert and also throwing an exception
Using the assert function and throwing an exception is the preferred method. In this article, we'll discuss why it's preferred and how to implement it effectively.
Usefulness of designing by contract
DbyC helps you catch bugs early in two places (Listing A):
- In your DbyC function, if results are wrong for some arguments
- In client code, when you call a DbyC function and you mistakenly pass an incorrect combination of arguments
You should use preconditions when your function does not handle some input data; for instance, when the data wouldn't make sense for that function (e.g., calling a factorial function with a negative value). You should use postconditions to test the correctness of the function output (e.g., the factorial function should return a positive value).
Saying that a contract is broken (it failed a precondition or postcondition) is equivalent to declaring a programming error. It means that either the function or the client code calling the function contains a semantic error. Usually, when either condition fails, the code that is supposed to be implemented after that failed condition should not be executed because the program will most likely crash (Listing B). In light of that possibility, when a DbyC precondition or postcondition fails, you'll want to:
- Find out ASAP that the contract has been broken. This is usually accomplished by breaking into the offending line of code with a debugger.
- Make sure that code following the broken contract is not executed (since the program could crash or produce invalid results). This is usually accomplished by throwing an exception.
If you're developing an application, you'll usually prefer to use the debugger method, because when a bug appears, you'll want to know about it right away. This will be very helpful, especially when connecting different modules from a big application (integration testing).
When you're developing a library, you never know where it will be used. For instance, if someone using your library is building a server application, the debugger option is not viable; therefore, your best bet is throwing an exception.
The biggest problem with the first option is that your program could crash at the customer site. Many experts recommend the second option over the debugger option for this issue alone. I tend to agree more and more with these experts—throwing an exception will make your program safer (Listing C).
Best of both worlds
The bad thing about throwing an exception, however, is that when a contract is broken, you'll find out too late and lose the context in which the problem occurred (Listing D).
Remember that contract is broken is equivalent to programming error. So, before throwing an exception, you can do something extra to retain the context of the problem:
- In Debug mode, break into the debugger when a contract is broken. You won't lose any context, and you'll be able to fix the problem very easily.
- In Release mode, find out where a broken contract occurred and log it somewhere. The program will then continue, and there will be no crash.
Achieving the above is simple: In your code, instead of using throw some_exc(args), just use dbyc_throw some_exc(args). The code for the dbyc_throw macro is shown in Listing E.
When you want to implement custom handling before an exception is thrown, you just define the THROW_CUSTOM_HANDLER macro prior to including <enhance_throw.h>. Then, in a source file, provide the before_throw_exception function.
Listing F shows you how to use the dbyc_throw method.
Another way
As you've seen, the code in <enhance_throw.h> is quite small and simplistic, but it does handle most cases. However, if it's not enough for you, the SMART_ASSERT library offers you more options and focuses on providing you with as much information as possible when a contract is broken. In addition, you can specify multiple assertion levels and set how each should be handled. Finally, you can use SMART_ASSERT in Release mode as well.
Choose wisely
DbyC is a very powerful paradigm, and there are many ways to implement it in C++. But you should do it wisely. As soon as a contract is broken, make sure you know about it firsthand and use <enhance_throw.h> or the SMART_ASSERT library, or implement your own library based on what you've seen in this article.
Print/View all Posts Comments on this article
|
|
|
|
White Papers, Webcasts, and Downloads
- VMware Infrastructure: A Guide to Bottom-Line Benefits VMware Frustrated by the high cost of maintaining or building ever-larger data centers? Get the facts you need to formulate your Virtualization Action Plan. Download Now
- The True Costs of Virtual Server Solutions VMware Discover ways to streamline and simplify your assessment of the total acquisition costs of a server virtualization environment. Download Now
- Leveraging SMB ERP for an Economic Recovery ZDNet Times are tough but better days are sure to follow. In the wake of an ... Download Now
- Why Isn't Server Virtualization Saving Us More? A Few Small Changes May Dramatically Increase Your Efficiency VMware Ever wonder why your company isn't saving more from its server virtualization? Making a few small changes could dramatically increase your efficiency. Download Now
- Building the Virtualized Enterprise with VMware Infrastructure VMware This paper explains how adopting a virtual infrastructure -- comprised of server, storage, and networking virtualization technologies -- can help your organization build a sustainable competitive ... Download Now
Article Categories
- Security
- Security Solutions, IT Locksmith
- Networking and Communications
- E-mail Administration NetNote, Cisco Routers and Switches
- CIO and IT Management
- Project Management, CIO Issues, Strategies that Scale
- Desktops, Laptops & OS
- Windows 2000 Professional, Microsoft Word, Microsoft Excel, Microsoft Access, Windows XP,
- Data Management
- Oracle, SQL Server
- Servers
- Windows NT, Linux NetNote, Windows Server 2003
- Career Development
- Geek Trivia
- Software/Web Development
- Web Development Zone, Visual Basic, .NET

