Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Debugging Support

Because CObject is the base class for almost all other classes in the MFC, it serves as a convenient place to stash a few very important debugging features.

AssertValid()

If you want to be able to verify that an object of your class is valid, you can override the AssertValid() member function of CObject. Like many other debug features, this should only be implemented in builds where the preprocessor symbol _DEBUG is defined. To implement an

AssertValid() function for your class, the declaration for your class should look like the following:

class CMyClass : public CObject
{
// other stuff for your class
public:
#ifdef _DEBUG
    virtual void AssertValid() const;
#endif
};

Now you need to implement your AssertValid() function. This function should perform a quick check that the elements of your class are in order. Your implementation should look something like this:

#ifdef _DEBUG
void CEmployee::AssertValid()
{
    // validate the base class
    CObject::AssertValid();
    // validate this class
    ASSERT(m_EmpNo != 0);
}  // end CEmployee::AssertValid()
#endif

You should perform all your validity tests with the ASSERT macro (and derivatives like ASSERT_KINDOF). You can use AssertValid() in your applications by calling the ASSERT_VALID macro, which takes a pointer to the object to be validated. This macro, like many other debug macros, will not generate any code in non-debug builds, so you won't need to hassle with all those pesky #ifdefs.

The ASSERT Macro

Because I have mentioned asserts several times already, it's about time you look at them in more detail. The ASSERT macro takes any expression that evaluates to a Boolean expression. If this expression is true (nonzero), all is well and the app goes on its merry way. If, however, the expression is false (0), a dialog box like that in Figure 2.9 will appear.

02fig09.gif

Figure 2.9 The Assertion dialog box.

This dialog box gives you three choices. You may choose Abort to stop your app right there; or you can choose Ignore to close the dialog and press on; or you can choose Retry, which enables you to jump right into the debugger at the point that the ASSERT failed, even if you did not start the application in the debugger. From this point, you can easily use the Call Stack window to figure out just where in your code things went amiss.

Remember that the ASSERT macro does not generate any code in non-debug builds. It will not even evaluate the expression. If you want to have the expression evaluated in release builds, you can use the VERIFY macro instead.

Dump()

Another important debugging feature of the CObject class is the Dump() function. This is useful when you want to spit out information about the current state of your class object periodically, or when you have noticed a problem. Once again, this feature should not be implemented in non-debug builds. To use the Dump() function in your classes, you insert code something like this:

class CEmployee : public CObject
{
public:
#ifdef _DEBUG
    virtual void Dump(CDumpContext& dc) const;
#endif
    int m_EmpNo;
    CString m_Name;
    // Other class stuff
}  // end class CEmployee

Your implementation would include code such as this:

#ifdef _DEBUG
void CEmployee::Dump(CDumpContext& dc) const
{
    // first, call base class Dump
    CObject::Dump(dc);
    // then dump this class
    dc << "Employee Number: " << m_EmpNo << "\n"
        << "Employee Name: " << m_Name << "\n";
}  // end CEmployee::Dump()
#endif

If you are running your application under the Visual C++ debugger, MFC will set up the dump context passed to Dump() to send its output to the Output window of the debugger.

The insertion operator for the dump context that is used in the examples is defined for the following types:

If the type you want to dump isn't supported, you can generally get by with a simple cast. After all, the data is just going to be converted to a string for output.

The TRACE() Macro

In addition to the Dump() function, your application can write to the debug context with the TRACE() macro provided by MFC. The TRACE() macro works much like printf(). It accepts a string that may include placeholders for other variables, using the same % variables that printf() uses, except TRACE() does not support floating-point variables. The TRACE() macros will, however, support Unicode strings.

With the MFC Tracer application (TRACER.EXE), included in the Tools menu of Developer Studio, you can enable or disable several sorts of trace messages provided by MFC's internals, as well as disable tracing altogether.

If you wish to assign MFC's dump context—and the TRACE() and Dump() output that goes with it—to something other than the debugger's Output window, see the documentation for the CDumpContext and Dumpinit.cpp in the MFC source directory, which declares CDumpContext afxDump. Unfortunately, the details of doing this are beyond the scope of this book.

Share ThisShare This

Informit Network