C/C++ Users Journal Cover from September, 2001 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.

CODE:
  1. PayMaster> Open Employee.dat
  2. TRACE: fopen() returns 0x50691100
  3. TRACE: get_index() returns OK
  4. 1534 records
  5. Paymaster>

Figure 1
A hypothetical program session with trace turned on

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 DrawText() or 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 name like debug.h:

CODE:
  1. #ifndef _DEBUG_H
  2. #define _DEBUG_H
  3.  
  4. HWND CreateDebugWindow( char *name ) ;
  5. void DestroyDebugWindow( HWND handle ) ;
  6. int SendDebugText( HWND, char *fmt, ... ) ;
  7.  
  8. #endif /* #ifndef _DEBUG_H */

Figure 2
A typical header file for a home-built trace package

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.

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.

Using the 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 Open() or 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 Open() method

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:

C++:
  1. Log <<"Success = " <<success <<"\n";
  2. Log <<"Value = " <<value <<"\n";

If the 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

The ConStream class is actually quite simple. The entire implementation consists of a single header file, ConStream.h and a corresponding C++ source file ConStream.cpp. 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:

C++:
  1. class ConStream
  2. #ifdef _UNICODE
  3.   : public basic_ostream<wchar_t>
  4. #else
  5.  : public basic_ostream<char>
  6. #endif
  7. {

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, but 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 AllocConsole() API 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 _open_osfhandle() function call. That handle is then converted to a FILE pointer with another non-standard library call: _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 filebuf constructor is one that provides some glue between the old C standard library and the new C++ iostreams library.

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 ConStream 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.

When the 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 to fopen().

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 in ConStreamDemoDlg.cpp, you can see how simple it is use write new data to the ConStream window. It's definitely a step up from using OutputDebugString().



Figure 5
ConStreamDemoDlg in action

If you are an NT user, you can change the demo program to a Unicode program by simply defining the _UNICODE macro, and changing the program entry point (as described in VC++ online help.)

Conclusion

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.

Downloads

ConStreamSource.zip The header and implementation files that you can use to add ConStream to your C++ project.
ConStreamDemo.zip The Win32 executable file that demos the class.
ConStreamDemoSource.zip The source code for the demo program so that you can tinker with it.