Skip to content

C++ Debug Macros

August 9, 2012

Introduction

I have never been much of fan of debuggers. I trace my antipathy back to my first real programming job, which was working for the Computer Services department of a large UK Polytechnic. The Poly had an eclectic mix of processors – a DEC-10, several VAXen, the odd Prime, a UNIX box here and there, and a couple of IBM 4381s. Not to mention all the different microcomputer and PC types which abounded in the early-  to mid-1980s. And all of these had their own operating systems, each with  its own debugger (often more than one), all of which worked differently, and had a different command set. As someone who programmed on all the above (except the Primes), I never really found time really to get to grips with the debuggers, and my habit of not using debuggers became engrained.

So, if I don’t generally use debuggers, how do I debug things? Well, by the time-honoured mechanism of reading the code, thinking hard, and then fixing it, of course! But sometimes this fails, and I need to print out values of variables and the like. This article describes some mechanisms for doing that using C++ and the macro pre-processor. In order to follow it, you will need a modicum (not very much) of C++ knowledge.

A Stream Debug Macro

As mentioned above, I do most of my debugging by printing out values, often to standard output. To facilitate that, I use the following C++ macro to print out variable values; I define it in a header file called dbgmsh.h:

#define DBGVAR( os, var ) \
  (os) << "DBG: " << __FILE__ << "(" << __LINE__ << ") "\
       << #var << " = [" << (var) << "]" << std::endl

and I use it in code like this:

#include "dbgmsg.h"

. . . .

string name = "foobar"; DBGVAR( cout, name );

which results in this output (on the cout stream, in this case):

DBG: testfile.cpp(20) name = [foobar]

The format  of the message is the string "DBG:" to indicate it is a debug message (and to make it easy to grep for in logs and the like), the name of the file containing the use of the macro (in this case, testfile.cpp), and the line number in that file it was used at, the name of the variable and then the variable’s value enclosed in square brackets (helpful for strings and the like which may contain trailing whitespace).

How Does It Work?

If you are not too familiar with C++ macros, a little explanation is in order. This line:

#define DBGVAR( os, var ) \

defines a macro called DBGVAR (in C and C++, macros traditionally have their names spelled out all in caps) which takes two parameters, which will be called os and var in the body of the macro. The backslash at the end of the line acts a sa continuation character – by default  macros can only consist of a single line, but the backslash escapes the end-of-line character and so allows multiple lines.

The bulk of the macro is in the next two lines. The first bit is simple:

(os) << "DBG: "

This is just using a C++ ostream to output something – you should be familiar enough with code like the following (the reason for the brackets around the ‘os’ parameter I’ll explain shortly):

cout << "hello world"

The next bit may be slightly less familiar:

<< __FILE__

This outputs the name of the file that the macro is used (not defined) in. The special name __FILE__ is a macro implemented by the preprocessor itself. Note the use of double-underscores to say "this is a reserved name" – you must never use names containing double-underscores in your own code (or names starting with an underscore and an uppercase letter), as these may (probably will) clash with reserved names used by the preprocessor and compiler – such clashes can be very hard to diagnose.

There are a number of these macros defined by the preprocessor – here’s a list:

Macro Description
__FILE__ Name of file the macro is used in.
__LINE__ Line number in file the macro is used in.
__DATE__ Date that the macro was expanded – effectively the compilation date of the file.
__TIME__ Time the macro was expanded.
__func__ Not strictly a macro, and defined only for C++11 – the name of the function the macro is used in.

From the above, you can probably deduce what this does:

<< __LINE__ 

The next bit of the macro illustrates a feature of the preprocessor you may not be aware of:

<< #var 

The ‘#” character when used like this is the preprocesor stringising operator – it turns whatever it precedes into a double-quoted string. Remember that in the this example, the macro is used like this:

DGBVAR( cout, name );

so that the macro parameter var will take on the contents name. The stringising operator then turns this into the quoted string string "name", and the contents of the string are then printed on the stream. This handles output of the name of the variable, but we also need to output its value – this code does it:

<< (var)

As #var gets replaced with "name", so (var) gets replaced with (name), and normal C++ variable evaluation gives us its contents. But why the parentheses around the macro parameter? Well, this is an issue of operator precedence – we have no way of knowing exactly how the macro is going to be used – suppose it was used like this:

DEBGVAR( cout, a && b );

Arguably this is a misuse of the macro, but we’d still like it to print out something sensible. However, without the parens in the macro definition, what you will get is a compilation error along the lines of:

invalid operands of types 'int' and 'const char [2]' to binary 'operator<<'

The problem is that the && operator has a lower precedence than the << operator, and so the macro is incorrectly evaluated. Putting the parens around the parameter evaluation solves the problem. In general, you should _always_ parenthesise macro parameter evaluations.

The last bit of the macro is this:

<< std::endl

Why use the endl manipulator rather than simply outputting a new line? Well, as we are debugging there is a possibility the program will crash soon (that’s why we’re debugging!), so we’d like the output stream to be flushed as often as possible – simply outputting a new line doesn’t flush the stream.

But Macros Are … Evil!

The "macros are evil" platitude gets trotted out with some regularity in C++ circles, but unfortunately there are some things that simply cannot be done without them. You may be wondering if the above code can be written with a template function instead of a macro, and yes some of it can be – you can write something like this:

template <typename OS, typename T>
void DbgVar( OS & os, const T & t ) {
    os << "DBG: " << t << std::endl;
}

But unfortunately, when you try to to introduce the __FILE__ and __LINE__ macros you will find that they expand to the file and line of the template definition, not the place where the template is used, which is what we need for debugging purposes.

Another Macro

As well as simply outputting variables, you often want to output more complex, possibly formatted messages. This second macro does that:

#define DBGMSG( os, msg ) \
  (os) << "DBG: " << __FILE__ << "(" << __LINE__ << ") " \
       << msg << std::endl

This is not used quite like previous macro – it might look something like this:

DGMSG( cout, "Value of pointer p is " << p << ", pointee is " << *p );

Note that the message uses the ostream streaming operator << to link together the various parts of the message, and that in this (rare) instance we don’t put parens round the macro parameter msg, as the result would be a syntax error.

Windows GUI Debugging

Windows GUI programmers may be looking a trifle askance at all this. after all, they don’t normally have a convenient stream like std::cout to write debugging messages on. This is true, though it is not hard to give a GUI application a console on which such messages can be written – but I’ll save that for another day. Arguably, Windows programmers  (both GUI and console) have something better – the OutputDebugString function, which allows you to write messages to your favourite debugger (I’ll address what is meant by a "debugger" a bit later).

OutputDebugString has a very simple interface – it allows you to write a single C-style string to the debugger:

OuputDebugString( "hello world" );

Unfortunately, many of the variables and messages we’d like to output won’t be C-style strings, so we need some way of converting them. And whenever you  you need to convert things to strings in C++, you should be thinking "std::stringstreams"! I have a tutorial introducing stringstreams here, and for the rest of this article I will assume you have read it.

Given a stringstream, converting the debugging macros to use OutputDebugString is very easy – here’s a version of DBGVAR that works with it:

#define WDBGVAR( var ) \
{ \
  std::ostringstream ods_stream_; \
  ods_stream_ << __FILE__ << "(" << __LINE__ << ") " \     
              << #var << " = [" << (var) << "]"; \
  ::OutputDebugString( ods_stream_.str().c_str() ); \
}

The big difference between this macro and the previously introduced ones is that that this one must create an ostringstream object. To do this, we need to create a new scope, in which the whole macro expansion operates. If this were not done, and you used the macro twice in the same enclosing scope, you would get multiple definition errors. So the macro contents here is enclosed in braces, creating a scope.

The main issue here is the name of the ostringstream object. This could clash with names in the outer scope, which might result in unexpected  output, or compilation errors. There is no perfect general solution to this – I simply use name for the stream that is very unlikely to appear in my own code, as I don’t use names containing underscores.

The remainder of the code is straightforward – instead of outputting directly to a stream like std::cout as in the previous macros, the output is sent to an ostringstream and then converted to a C-style string and passed to the Windows OutputDebugString function.

You might worry that defining an ostringstream object every time you want to output a message has a large overhead. Investigations I’ve performed indicate that while there is an overhead, it is overwhelmed by the actual formatting and output operations. And defining a new stream each time turns out (unintuitively) to be more efficient than using a single static stream.

What Is  A Debugger?

I said that OutputDebugString writes its output to a debugger. This begs the question, "what is a debugger?" Well, in this context it means anything that can understand the protocol that OutputDebugString uses (basically, it writes its output to a memory- mapped file, which the debugger reads). Most (but not all) Windows IDEs know this protocol and will display OutputDebugString messages in their debug output windows. However, a better tool for looking at these messages is probably the (free) DebugView utility provided by Microsoft as part of their excellent SysInternals tools (if you work on Windows and you haven’t got SysInternals stuff, stop reading right now and download them).  Here’s DebugView displaying a few messages:

Screenshot_DebugView on __OPHELIA (local)_2012-08-09_17-31-12

Obviously, one could also re-write DBGMSG to output to the debugger via OutputDebugString in much the same way.

Summary

This article has presented a few C++ macros that can usefully be used by people debugging both GUI and non-GUI programs and has provided a brief introduction to the Windows OutputDebugString API and the tools that are aware of it.

About these ads

From → c++, devtools, linux, windows

2 Comments
  1. Would wrapping your macros in a do { } while(false) not remove some potential for errors?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: