|C/C++ Users Journal September, 2001|
This article presents a class that attaches a C++
std::iostream object to a Win32 console
window. The window can be enabled or disabled at will, and provides an excellent vehicle for
dumping debug output. Now that the C++ iostreams specification is standardized, and compiler
vendors are implementing to the standard, this class should work properly with all future
versions of Win32 compilers.
Debugging the old fashioned way
I might be giving away some hints about my age, but my favorite debugging tool has always been
some variation on a
printf statement. This is probably because I formed most of my debugging
habits in the days before the creation of the windowed symbolic source debuggers everyone takes
for granted these days.
High powered debuggers are great tools, but unfortunately, you can’t always count on them being present on your target system. Even if you are able to install a debugger on all of your test systems, you also have to contend with different behavior in your debug builds. That bug you’ve been working on finding for so long might not show up when optimization has been turned off!
My way around these types of problems has usually been to have an unsophisticated trace function that I could turn on and off at runtime. During the development process I can quickly add trace statements in key spots and enable them later when trouble showed up.
When writing console mode batch programs with a command line user interface, it was easy to spew a few lines of text in between prompts. You’ve all seen interactive sessions with debug output like that shown in Figure 1.
PayMaster> Open Employee.dat TRACE: fopen() returns 0x50691100 TRACE: get_index() returns OK 1534 records Paymaster>
This sort of debug output is a little bit obtrusive, but all of the necessary user interface elements are still visible, so it makes for an acceptable solution.
Tracing with windows
The last 10 years have proven to be the death of the traditional command line oriented user interface. Once users got the hang of menu driven programs that ran in text windows, they were hooked. The subsequent takeover of the desktop by the Windows GUI simply put the final nail in the coffin. For most of these new programming environments, printing directly to the console just wasn’t an option any more.
The resulting migration to the Windows API exposed a couple of problems with my personal debugging techniques: lack of a suitable output window class, and lack of a flexible print function.
I’ve never understood why Microsoft doesn’t have a console text window class. It would be really convenient to be able to create a small window that you could simply print text messages to. Like a standard MS-DOS console, it would support fixed width fonts, scroll text when it received a line feed, and maybe obey the standard ANSI escape sequences. It would be a dream come true for debug output.
However, Microsoft seems to be uninclined to help in this area, so I’ve cobbled together my own versions of this sort of window from time to time for various projects. Unfortunately, I have never been entirely satisfied with the results. Doing the job right is not a trivial project, and usually ends up being a fairly significant consumer of code space and CPU time. I’ve also tried using variations on edit or list controls. Subclassing these controls is more efficient, but the resulting windows are usually hamstrung by a few missing capabilities.
The second problem is the lack of a suitable output routine. The nice thing about using
printf() for debugging was that I didn’t have to worry about allocating an intermediate buffer
to hold my output text. But writing debug output to a Win32 window means using a function like
vsprintf() to format the text to a buffer before using a function like
OutputDebugString() to actually render the string to a window. Using
vsprintf() implies that
you know in advance what your maximum buffer size is going to be. Worse yet, you don’t have any
way to check in advance for catastrophic buffer overruns.
Despite all this, until recently most of my big Windows projects had a function that created and
destroyed some sort of debug window, and a function or two for writing to that window. I would
package these up in a single source file, and had a simple interface defined in a file with a
Win32 and C++ bring big improvements
Over the past few years, most of my Windows work has migrated to the Win32 platform, with the
most frequent choice of a development tool being Visual C++. The combination of these two things
led to a major improvement in the way my debug output was managed: the
ConStream class neatly solves the two problems I talked about in the previous section.
First, it uses the Win32 API to create a text mode console window for output. This text window is
not a full fledged graphics window; it instead behaves just like the window you get when you open
an MS-DOS session. To format output to this window,
ConStream uses the standard overloaded
output functions found in the iostreams library for output.
ConStream class is engagingly simple. You simply create one object of the class and
make it visible everywhere it is going to be used. Since the
ConStream object doesn’t have a
message loop or a true Window, you don’t have to worry about whether it is owned by another
window, you can simply make it a global object.
At any point in your program, you can open or close the debug window by calling the
Close() member functions. When the window is created it will pop up and
look something like that shown in Figure 3.
Figure 3 — The Console window created by the
Writing data to the debug window is done exactly as you would expect with a C++ iostream object.
For example, if you have a global
ConStream object called
Log, you would send debug
information to it like this:
ConStream window is closed, sending these log messages has no effect, they are simply
dumped to the bit bucket. If the window is open, they are formatted and sent to the window just
like you would expect:
Figure 4 — Output to the Console Window
That means you can safely leave your debug message code turned on at all times, confident that the messages will only be displayed if you open your window. And those worries about buffer overruns evaporate, since formatting and management of output text is handled by the compiler’s bulletproof standard library.
How ConStream does it
ConStream class is actually quite simple. The entire implementation consists of a single
header file, ConStream.h and a
corresponding C++ source file
As you can see in the listing, the code is compiled slightly differently depending on whether the
UNICODE macro is turned on. When this macro is turned on for a Windows NT target, the debug
window will display wide characters of type
wchar_t instead of the standard char type. The ease
with which this is accomplished is a testament to the design of the C++ library.
The most important task for
ConStream is to format data that is sent to it using the ‘<<’
insertion operator. Naturally, we want to accomplish this by inheriting the behavior from
existing library classes. That is done in the first lines of the class definition:
You might not recognize the name of the class that
ConStream is derived from. Class
basic_ostream<T> is the templatized version of what you normally think of as class
ostream. Fairly late in the standardization process, the ISO C++ committee made the brave
decision to define the I/O classes in the library as template classes based on the character
type. While this made a lot of library code (although not application code) obsolete, it meant
that the I/O classes weren’t tied to any particular character type, which is a very good thing.
Inheriting from the
basic_ostream class is well and good, but when you insert characters into a
ConStream object, who decides where they go? There are a number of ways to direct stream data,
ConStream does it by attaching itself to old fashioned
FILE * objects from the
stdlib.h portion of the standard library.
If you examine the source to the
Open() function in ConStream.cpp, you can see that
the process works as follows. A console window is first created using the
call. The routine then gets an O/S handle for the console window. The O/S handle is then
converted to a low level stdlib.h handle with the non-standard
function call. That handle is then converted to a
FILE pointer with another non-standard
_fdopen(). (Note that these non-standard library calls are faithfully
implemented in the libraries of the other major Win32 compiler vendors.)
The good news is that after that long string of conversions, we finally have something that can
be used to initialize an
iostream object. The next line of code in the library creates a
basic_filebuf object, which is initialized with a
FILE pointer. This particular
constructor is one that provides some glue between the old C standard library and the new C++
The last step is to call the
init() function (a member of the base class) with the
basic_filebuf object as an argument. After all that work, any data inserted into the
object will now be printed in the console window. The final call to
setf() serves to set the
stream to automatically flush after every line is inserted.
ConStream object is first constructed, or after the
Close() member function is
called, there is no console window. In those cases, we use a similar process to direct the stream
to the bit bucket, by calling
fopen() on “nul”, the standard null output device. The
code that does this is a little bit simpler, because we get to skip the work needed to convert
the console handle into a
FILE pointer. Instead, we get the file pointer directly from the call
A sample program
I’ve written a very small sample program that demonstrates this class using Visual C++ 5.0. The
simple dialog box based program is shown in Figure 5. The dialog has a single check box that can
create and destroy the debug window. Once the window is opened, debug statements are scattered
around the program in a few key places, including the handlers for gaining and losing focus in
the edit control, and for the Display button. If you look at the code in the main program, shown
you can see how simple it is use write new data to the
ConStream window. It’s definitely a
step up from using
If you are an NT user, you can change the demo program to a Unicode program by simply defining
_UNICODE macro, and changing the program entry point (as described in VC++ online help.)
There is obviously a lot you could to spiff up this debug facility. But to me, the best thing
about it is its simplicity. I really like being able to add this to a program by simply including
two new files in my project, then adding a simple data object to my main window. Better yet, the
simplicity ensures that I don’t spend any time trying to debug my debugging code! So until
Microsoft unveils some improved versions of the console window, I’ll probably keep using the
ConStream class as is.