Modern DebugStr
Volume Number: 12
Issue Number: 9
Column Tag: Debugging Aids
DebugStr, the Modern Way
Capturing your program’s iostream of consciousness
By Jon Kalb
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
I have a friend who spends most of his time in cutting-edge (read, “reliable tools not
yet available”) environments. He likes to say: “There is no debugger; there is only
DebugStr.” While he may stretch the truth, he has a point. Our friends who build
source-level debugging tools are a heroic lot, but there are times when MacsBug is the
only option. This article, along with the accompanying dout Library, is one attempt to
make using DebugStr a little more modern.
Using DebugStr is usually a clumsy process of converting integer values to
strings and concatenating Pascal strings. There had to be a better way, I thought.
That’s when it occurred to me to implement the low-level debugger as a C++ output
stream.
The Solution Domain
This approach may be valuable for two groups: fellow users of DebugStr, and
individuals interested in using and/or implementing C++ streams. But first, some
limitations:
• I use the term “library” rather loosely. The dout Library is really just a small
collection of source files. I also include project files for both Metrowerks and
Symantec, to demonstrate how to use the library.
• In no way does this library replace a source-level debugger. If it is possible for
you to use one, do so.
• Although, for the sake of clarity, I don’t follow this practice in this article, in
real code your should always surround every use of the dout stream with some
type of #ifndef NDEBUG statement. (NDEBUG is the preprocessor variable that
controls the assert macro found in assert.h. Define NDEBUG for shipping code,
and leave it undefined for code under development.) To make this task a little
easier, the dout Library includes the data statement macro, ds. For example,
the line “ds(dout << myObject);” would be completely stripped if NDEBUG was
defined, and would become “dout << myObject;” if NDEBUG wasn’t defined.
• Streaming the semicolon character ';' to dout is problematic. MacsBug
interprets the text following a semicolon as a MacsBug command, and attempts to
execute text that you intended to display.
• Below are examples of overloading the insertion operator for objects to support
debugging in a streams environment. This is possible if your code base doesn’t
already use insertion operator overloading for other purposes. It may be
possible to use the same routine for, say, storing an object to disk and for
debugging purposes, but it seems unlikely. I suggest a work-around later.
• Streaming data to dout in the constructor of a static or global object (an object
that is constructed before the first line of main is executed) is problematic.
More on this below.
Using the Library
To use the dout Library you must include the standard C++ iostreams and the source
file debugbuf.cp. Each source file that writes to the dout stream must include the
header file doutstream.
Standard Streams
You can use dout just as you would cout. For example:
#include "doutstream
// ...
dout << "file corrupted at offset " << byteOffset << endl;
// ...
dout << "name: " << un << endl << "password: " << pw << endl;
// ...
dout << "completed pass " << i << " of " << total << endl;