Delen via


Thinking about the problem (C++ Reflection, part 1)

Setting Goals

So you want to build a reflection system in C++? Well, I assume so since you're reading this. =) Ok, enough jokes...

There are a few ways to go about building a reflection system in C++ and there are different levels that we can accomplish. For instance, to create a reflection based system that works without any modification to standard C++ types and classes would be very challenging, to say the least. In order to help us with this problem, we'll start off with a set of goals so that we know what we want to aim for.

  1. Maintainable - It would be nice if (and when) we need to make modifications or bug fixes the code base is clean and logical.
  2. Robust - I don't like when things only work when sprinkling some magic pixie dust, so let's make this thing as robust as possible. This means we'll be using templates rather than MACROs to do our magic.
  3. Effecient - The idea isn't to create a reflection system takes five minutes to perform something, we want a small and fast system.
  4. Flexible - Let's shoot for making an extensible system so that we can add to it later without too many problems.

I think that those goals are good enough to get started.

Where to Start

In order to being programming this thing we need to understand what type of information we want. For now the only things that we will concern ourselves with are unique ID for the class and the name of the class; we'll add to it later to actually make it do some cool stuff.

To start off we'll need the following classes to provide us with some basic run-time information: RuntimeInfo and RuntimeClass.

    1:  struct RuntimeInfo
    2:  {
    3:      RuntimeInfo( unsigned int classId, const char* name );
    4:   
    5:      static const unsigned int CLASS_NAME_LENGTH = 32;
    6:   
    7:      unsigned int ClassId;
    8:      char         ClassName[ CLASS_NAME_LENGTH ];
    9:  };

This class will define all of the cool details for our class; currently we only need to know the ID for the and the name for the class. The ID could be used to create a new type of class or to compare is two classes are the same.

    1:  template< class T, class BaseClass, unsigned int ClassId >
    2:  class RuntimeClass : public BaseClass
    3:  {
    4:  public:
    5:      static inline RuntimeInfo* GetRuntimeInfo() { return &_info; }
    6:   
    7:  protected:
    8:      static RuntimeInfo _info;
    9:  };
   10:   
   11:  template< class T, class BaseClass, unsigned int ClassId >
   12:  RuntimeInfo
   13:  RuntimeClass< T, BaseClass, ClassId >::_info( ClassId, typeid( T ).name() );

I hope you're up to speed with your templates =). This class is a tricky way of injecting information that is shared by each type of class we'll be using in our reflection system. All this class currently has is the runtime information that is created based on the templated information.

The next step is to create the base class we'll be using for all of the objects in our system; it's appropriately named Object.

    1:  class NullObject{};
    2:  class Object : public RuntimeClass< Object, NullObject, 0x0A >
    3:  {
    4:  public:
    5:      Object();
    6:      virtual ~Object();
    7:  };

One of the first things to notice is that it implements our RuntimeClass and passes in the type that it is, the type it derives from, and the ID to use for the class. As you can see, there is another class NullObject here; this is used so that are template parameters will be satisfied.

That's it! I've given a very brief breakdown of what it takes to get started building a reflection system. Next time we'll learn how set it up so we can create new instances and set some properties on our classes. I'll leave you with a little sample app that shows it working.

    1:  class MyClass : public RuntimeClass< MyClass, Object, 0xF0 >
    2:  {
    3:  public:
    4:      MyClass();
    5:      virtual ~MyClass();
    6:  };
    7:   
    8:  int
    9:  _tmain( int argc, _TCHAR* argv[] )
   10:  {
   11:      Object o;
   12:      MyClass c;
   13:   
   14:      cout << "-- OBJECT --" << endl;
   15:      cout << "\tClass ID: " << o.GetRuntimeInfo()->ClassId << endl;
   16:      cout << "\tClass Name: " << o.GetRuntimeInfo()->ClassName << endl;
   17:      cout << endl;
   18:   
   19:      cout << "-- MYCLASS --" << endl;
   20:      cout << "\tClass ID: " << c.GetRuntimeInfo()->ClassId << endl;
   21:      cout << "\tClass Name: " << c.GetRuntimeInfo()->ClassName << endl;
   22:      cout << endl;
   23:   
   24:      cout << "Press enter to exit." << endl;
   25:      cin.get();
   26:   
   27:      return 0;
   28:  }