Reflection in MFC

Of course we could try to imitate Java in C++ by introducing our own meta classes. In fact, we can find meta classes in several proprietary C++ libraries, including Microsoft Foundation Classes (MFC), which is a framework for developing Windows applications in C++. Almost all MFC classes derive from the CObject[1] base class, which is roughly similar in purpose to Java's Object base class:

class CObject
{
public:
   virtual CRuntimeClass* GetRuntimeClass() const;
   bool IsKindOf(const CRuntimeClass* pClass) const;
   // etc.
};

Any CObject-derived class, A, will redefine the virtual GetRuntimeClass() function, which returns an instance of CRuntimeClass that represents A. MFC's CRuntimeClass is roughly similar to Java's Class class:

class CRuntimeClass
{
public:
   char* m_lpszClassName; // = class name
   CObject* CreateObject();
   bool IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
   // etc.
};

Example

Returning once again to our musical example, let's redefine Note as a subclass of MFC's CObject base class:

class Note: public CObject
{
public:
   DECLARE_DYNCREATE( Note )
   Note()
   {
      frequency = 60;
      duration = 300;
   }
   virtual void play()
   {
      cout << "playing a generic note" << endl;
   }
private:
   int frequency; // in Hz
   int duration;  // in millisecs
};

DECLARE_DYNCREATE is one of many macros provided by MFC. This one expands into the declarations of the member functions needed to support reflection. The macro must also appear in the subclasses of Note:

class HornNote: public Note
{
public:
   DECLARE_DYNCREATE( HornNote )
   void play()
   {
      cout << "playing a horn note" << endl;
   }
};

class ViolinNote: public Note
{
public:
   DECLARE_DYNCREATE( ViolinNote )
   void play()
   {
      cout << "playing a violin note" << endl;
   }
};

We place the macros that expand into the implementations of these member functions in the corresponding implementation file (e.g., note.cpp):

IMPLEMENT_DYNCREATE( Note, CObject )
IMPLEMENT_DYNCREATE( HornNote, Note )
IMPLEMENT_DYNCREATE( ViolinNote, Note )

Here are some of the include directives we will need:

#include <afx.h> // MFC library
#include <iostream>
using namespace std;

We will also need to tell the linker to link the MFC library with our program.[2] (Of course the MFC library is only available in certain integrated development environments such as Visual C++.)

Our test driver, main(), begins by creating a Note object, then printing the name of its class:

Note* note = new Note();
CRuntimeClass *rtclass = note->GetRuntimeClass();
cout << "class = " << rtclass->m_lpszClassName << endl;

Here's the output produced:

class = Note

Next, we point the note pointer at two other notes and print the class name:

note = new HornNote();
rtclass = note->GetRuntimeClass();
cout << "class = " << rtclass->m_lpszClassName << endl;
  
note = new ViolinNote();
rtclass = note->GetRuntimeClass();
cout << "class = " << rtclass->m_lpszClassName << endl;

Here's the output produced:

class = HornNote
class = ViolinNote

We can also ask if an object belongs to other classes. We form these classes using the RUNTIME_CLASS macro:

cout << "Is CObject? = ";
cout << note->IsKindOf(RUNTIME_CLASS(CObject)) << endl;
cout << "Is HornNote? = ";
cout << note->IsKindOf(RUNTIME_CLASS(HornNote)) << endl;

Here's the output produced:

Is CObject? = 1
Is HornNote? = 0

Reflection in MFC goes beyond runtime type identification. For example, we can dynamically create objects from meta objects.

rtclass = RUNTIME_CLASS(HornNote);
note = (Note*)rtclass->CreateObject();
cout << "class = " << rtclass->m_lpszClassName << endl;

Here's the output produced:

class = HornNote

 



[1] MFC names usually begin with a letter that indicates their type. CObject is the name of a class, so it begins with "C".

[2] In VC++ select Settings from the Project menu. In the General page select "Use MFC in a shared DLL" in the Microsoft Foundation Classes drop down list.