Hook Up MFC Diagnostics

Since most MFC classes are ultimately derived from class CObject, they inherit several useful diagnostic features. The two most important of these are the ability to:

You can easily take advantage of these abilities in your documents, views, and other classes derived from MFC base classes. All classes derived from CObject, directly or indirectly, inherit — and can override — two member functions: Dump and AssertValid. AppWizard assumes that you'll want to make these overrides and so writes shells for the functions for you.

Dumping an Object's Contents

During development, you'll often want to know what an object's member variables contain. Override the Dump member function in your derived class to dump the class's contents to a "dump context." (Consult the discussion of diagnostics in your documentation to see where dumped contents are sent. You can search for the afxDump global variable.)

For example, even before you begin to move SHOWDIB's code into the document and view classes, you can override CShowDibView::Dump, something like this:


#ifdef _DEBUG void CShowDibView::Dump(CDumpContext& dc) const { CView::Dump(dc); // dump view object contents dc << member_variable_1; // pass variables to dump context dc << member_variable_2; member_variable_3.Dump(); // or call the Dump function of any // class objects contained by your class //... } #endif //_DEBUG

Notice that Dump can only dump variables visible to it. Local variables inside member functions of CShowDibView aren't visible. But member variables of the class are visible. If a variable constitutes part of what you consider the class's state, it's a good idea to make it a class member variable.

Dump is always conditionally compiled for debug builds. MFC passes you a CDumpContext object, which works something like a C++ iostream object like cout. You "insert" variables to the dump context with the CDumpContext insertion operator (<<); insertions can be chained. CDumpContext accepts many common data types. You can regulate the "depth" to which objects are dumped. If you want to dump object-type data with its own Dump function, you can call the object's Dump function, passing your CDumpContext object. For more information about dumping, see the article "Diagnostics" in Programming with the Microsoft Foundation Class Library (Visual C++ 2.0) or Chapter 15 in the Class Library User's Guide (earlier versions).

Validating an Object's State

C++ class objects can be internally very complex. When you construct an object, or receive one as a parameter to a function, you'd like to be able to validate the object's internal state for some sort of consistency or reasonableness. Override the AssertValid member function in your derived class to enable objects of the class to report their own internal validity. Used consistently throughout your CObject-derived classes, AssertValid is a powerful diagnostic tool.

In your AssertValid override, you usually use the ASSERT macro to test assumptions about the class's member variables. In debug builds, ASSERT tests a Boolean condition. If the condition is FALSE, the macro terminates the application with a message. Otherwise, execution continues normally. In release builds, ASSERT does nothing (and arguments passed to it are not evaluated). For example, an AssertValid function might look like this:


#ifdef _DEBUG void CShowDibView::AssertValid() const { // assert validity of base class contents CShowDib::AssertValid(); // assert the validity of object contents ASSERT(m_nValue == 0); ASSERT_VALID(m_pSubObject); // ... } #endif //_DEBUG

Like Dump, AssertValid is conditionally compiled for debug builds. The function first calls the base class version to test any variables declared in that or higher base classes. Then it tests the validity of the derived class's member variables.

Tip If you need ASSERT functionality in a release build, use the VERIFY macro instead.

When one of your functions is passed an object that overrides AssertValid, use the ASSERT_VALID macro to invoke the object's AssertValid function:


... ASSERT_VALID(pAnObject); // calls pAnObject's AssertValid ...

If your class has member variables that contain pointers to other objects, you can also use ASSERT_VALID inside your AssertValid function, as shown above, to validate those objects. An ASSERT_VALID call can often work its way down through several layers of the AssertValid functions of embedded or contained objects. Figure 9 shows this process for the example that follows.

For example, suppose you have a CObList collection containing pointers to CPerson objects, and class CPerson, like CObList, overrides AssertValid. You can use this AssertValid function to check the internal consistency of the list. With a little more work, you can even have your list (a CPersonList class derived from CObList) call the AssertValid function of each contained CPerson object. The CPerson objects might in their turn call the AssertValid functions of subordinate objects that they contain. Here's a small fragment of code that gets the whole process going:


// Given a pointer, pList, to a CPersonList of CPerson pointers... ASSERT_VALID(pList); // The rest flows from that call

In a debug build, you aren't worried about the overhead of all this validation, and the easy validation is very valuable diagnostic information. For more information, see the article "Diagnostics: The ASSERT Macro" in Part 2 of Programming with the Microsoft Foundation Class Library for Visual C++ 2.0, or for earlier versions see "Overriding the AssertValid Function" in the Class Library User's Guide.