Click here to Skip to main content
Click here to Skip to main content

Debugging and testing made easy. (Part 1)

, 9 Mar 2003
Rate this:
Please Sign up or sign in to vote.
Some macros and tips to take the air out of bugs, and classes to make unit testing painless and simple.
<!-- Add the rest of your HTML here -->

Introduction

How often have you found yourself in the situation that you are debugging some code and everything seems logical but it just doesn't work? Quite often as a beginner. Well, I found myself in this situation much less these days because of some great tips I have picked up. I want to share them with you'll and hope they will help you'll too.

Background

I program in VC++, but quite often I find myself writing console utilities for various stuff. VC++ and MFC have some great macros (c++ #defines) to help you out in debugging. But if you aren't using MFC in your project, then you are stuck. Well, those macros aren't a lot of magic and are quite easy to reproduce for your non-MFC projects. (and non VC++ projects too.)

One of the biggest reasons (according to me, at least) there are bugs in the code, is incorrect assumptions about what a particular piece of code is supposed to (and not supposed to) do. So the way out is document your assumptions in code, and make your code give you warnings when it is not working as expected, or is being used (or misused) in a wrong way.

Some theory (I hope its not boring). Bertrand Meyer introduced the concept of design by contract to do exactly the above. (and much more, but we don't want to get in those gory details in this article.) But c++ does not support contracts like the way eiffel does. So what do we do. We try to come up with our ways of implementing contracts.

What the hell is this contract I'm talking about?  I don't need no legal mumbo jumbo for programming. You don't. Basically a contract for a function (or method) indicates what conditions it expects to be satisfied to be able to work correctly, what conditions that are imposed upon it by the caller, and finally if there are some global conditions which shouldn't be violated.

The conditions which must be satisfied for a function to work correctly are its preconditions or "REQUIRE"-ments. For e.g. arguments shouldn't be null is a very common requirement. The conditions imposed by the caller of a particular function must be met. This is to "ENSURE" that result is as expected. For e.g. if function involves working with files, that the operations were indeed completed, and not aborted. The global conditions are typically called invariants or "ASSERT"-ions.

When we are debugging or in development, we want to be informed when the contracts are broken. Because when the contract is broken, it indicates that the result may not necessarily be correct and something is amiss. One of the easiest way is top print messages to the console like preconditions not satisfied, assertions failed, or so and so variable has this value while this assertion failed. Well here are some macros that do exactly that.

#define DBGOUT cout //you could just as easily put 
                    //name of a ofstream here and log everything to a file.

#define REQUIRE(cond) \
if (!(cond))\
{\
    DBGOUT << "\nPrecondition  \t" << #cond << "\tFAILED\t\t" \
           << __FILE__ << "(" << __LINE__ << ")";\
};\

//precondition tracing macro

#define ENSURE(cond)\
if (!(cond)) \
{\
    DBGOUT << "\nPostcondition \t" << #cond << "\tFAILED\t\t" \
           << __FILE__ << "(" << __LINE__ << ")";\
};\

//postcondition tracing macro

#define ASSERT(cond) \
if (!(cond)) \
{\
    DBGOUT << "\nAssertion     \t" << #cond << "\tFAILED\t\t" \
           << __FILE__ << "(" << __LINE__ << ")";\
};\

//invariant tracing macro

#define TRACE(data) \
    DBGOUT << "\nTrace         \t" << #data << " : " << data \
           << "\t\t" << __FILE__ << "(" << __LINE__ << ")";

//dump some variables value.

#define WARN(str) \
    DBGOUT << "\nWarning       \t" << #str << "\t\t" \
           << __FILE__ << "(" << __LINE__ << ")";

//print some warning indicating that some code is being executed which <BR>//shouldn't really be executed.

Using the code

Lets take the example of a function to divide two numbers and see where these macros would help us.

int div (int a, int b)
{
  //preconditions
  REQUIRE(b != 0)

  int result = a/b;

  //postconditions
  ENSURE(b*result <= a)

  return a/b;
}

The above example may look trivial, but consider more complicated functions, and with proper use of the above macros you will at least the most common causes of errors. TRACE and WARN macros will be particularly useful in figuring out why something is not what it should be, and ENSURE, REQUIRE and ASSERT will give you confidence that your functions, if given correct input, will produce correct output.

Slightly more complicated example:

//to parse some string in numbers (thousand) to its equivalent integer(1000)
int Parse(char* str)
{
  //preconditions
  REQUIRE(str != NULL) //null strings cannot be parsed
  int result;

  //some table lookup is done here
  //assert that index is within array bounds
  ASSERT (lookupindex < tablesize)

  //postconditions
  ENSURE(result >= 0) // the caller doesnt expect negative answers
  return result;
}

Misuse

1. There are some drawbacks and misuse of these macros. The first is, they slow down processing. So, you typically don't want them in the release code, only while developing or debugging, in that case you conditionally define the macros to produce code in debug, but not in release (see the zip file, it is defined that way in the dbgmacros.h).

2. Secondly, the macros expect to test invariant conditions and don't expect some processing to be done in them. for e.g. ASSERT(i++!=10) this code will most possibly fail in you release. Don't do processing in the macros. Only check conditions.

Points of Interest

To dig further, I urge you to dig deeper into the design by contracts philosophy. It has tremendous impact on how object oriented systems are designed and implemented.

I will come up with similar macros to ease up your testing in the next part of this article, probably next week.

History

  • First revision, March 10th, 2003.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Kumarpal Sheth
Web Developer
India India
No Biography provided

Comments and Discussions

 
GeneralModAssert is better PinmemberJasonReese18-May-06 1:42 
Generalif-else parse error PinmemberGisle Vanem12-Mar-03 8:41 
GeneralRe: if-else parse error PinmemberKumarpal Sheth12-Mar-03 18:06 
GeneralRe: if-else parse error PinmemberVictor Boctor29-Sep-03 21:58 
GeneralRe: if-else parse error PinmemberGisle Vanem29-Sep-03 22:30 
GeneralInteresting article Pinmembernerd_biker12-Mar-03 4:30 
GeneralRe: Interesting article PinmemberKumarpal Sheth12-Mar-03 8:27 
GeneralRe: Interesting article Pinmemberjerikat30-Dec-03 6:58 
GeneralRe: Interesting article - Correction! PinmemberCorwin of Amber11-Sep-06 10:04 
GeneralI found it helpful. Pinmember73Zeppelin10-Mar-03 16:35 
GeneralRe: I found it helpful. PinmemberKumarpal Sheth11-Mar-03 5:48 
GeneralRe: I found it helpful. Pinmembernerd_biker12-Mar-03 4:11 
GeneralNot full DBC PinmemberWilliam E. Kempf10-Mar-03 9:22 
GeneralRe: Not full DBC PinmemberKumarpal Sheth10-Mar-03 15:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 10 Mar 2003
Article Copyright 2003 by Kumarpal Sheth
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid