Whether we like it or no – C++ is an ancient language. It doesn‚??t have many features that are considered normal in some more modern languages. One of such missing features is reflection. It also never been lucky enough to get any kind of standard implementation. Over the years programmers, both main-stream & gamedev – have been patching their own systems. Today, I‚??d like to present yet another approach that I‚??ve developed.
The ‚??classic‚?Ě way, used in majority of engines out there (including some that I worked with) is to describe reflection using preprocessor/template magic. Nice example of this approach can be found in Game Programming Gems 5 article – ‚??Using Templates for Reflection in C++‚?Ě. Amount of additional code that we have to type is almost directly related to amount of data that we need. If all we want is to create some objects using type-name ‚?? it‚??s just a few lines here and there. Things get more complicated if we want to reflect fields/functions as well. In many cases we‚??re also limited by having single-root class hierarchy (ie, for the type to support reflection it has to derive from some kind of base ‚??Object‚?Ě type providing basic functionality). Here‚??s how it may look in a hypothetical preprocessor based solution:
Problem with all those solution is that they require a fair bit of maintenance. You need to remember about setting it up & keeping up to date. It can be tedious and bug prone.
I‚??ve been experimenting with various approaches for a while, but still wasn‚??t 100% satisfied. I wanted my ideal solution to have the following properties:
- require minimal (or none) amount of additional code when adding new types/fields,
- changes to existing types should be as easy as possible, ideally without any code modifications,
- parts of type-info should be optional. Editor may need much more informations than game for example. In some cases game may not need it at all, why pay for something that‚??s not being used?
It‚??s visible that those goals are hard to achieve using any kind of macro/template technique. Ideally, the reflection system would be totally detached from rest of the codebase and all the information loaded optionally. In order to achieve this, we can try to generate reflection database offline. Initially, I experimented with parsing C++ code with GCCXML. It‚??s possible to make it work, but resulting data would have to be re-parsed again (admittedly, that‚??s not so big problem, but being lazy bum I am, still wanted to avoid it). Later, I recalled that I‚??ve been using MSDIA quite extensively in my other projects, why not try it again? My rough plan was:
- extract reflection info from PDB files using MSDIA SDK,
- convert it to my internal format and save to file,
- load info in game/editor when requested,
Let‚??s go through these points one by one.
Extract reflection info from PDB files using MSDIA SDKA
Done using external application named¬† – you guessed it – Reflector. It operates on specified PDB file and list of types it should reflect. Then processes all UDTs & enums in PDB, when type from the list is encountered ‚?? it tries to dig out all the necessary information for this type and its fields. Process is semi-automatic, ie. if type Foo has field of type Bar, it‚??ll generate reflection info for Bar even if it‚??s not on the to-reflect-list (same applies to base classes).
Convert it to my internal format and save to file
Result of the program is reflection info database saved to binary file. It contains all necessary info like type name, size, list of fields for class types, number of elements for array types, list of constants for enums and so on. For class types it will also try to find a few crucial functions, I‚??ll write more about it later.
Load info in game/editor when requested
That‚??s the easy part, format is rather simple so it‚??s a matter of loading type info and creating internal description structures.
Here‚??s fragment of my test application, contains no preprocessor magic at all, reflection info is 90% independent from the rest of the code (100% if you don‚??t care about load-in-place and create by name). Application has Reflector set as a post-build step, so that reflection info is always up to date.
In the next installment I‚??ll write a little bit about simple load-in-place system I slapped on top of this code and publish the source, I have to clean it up first (another one of those quick experiments, so it’s quite messy).