To reflect or not to reflect.

Started by
3 comments, last by All8Up 12 years, 5 months ago
So, in the process of finishing off a new variation of my object system it came time to integrate it with tools and editors. This is always such a fun item, given that with C++ the obvious answers available in other languages are not so "obvious", i.e. reflection used to generate the UI's as property sheets etc on the fly. I've used several reflection systems for C++ mostly with mixed results. Some were a massive pain for maintenance reasons such as requiring external tools to generate reflection headers and of course any circular dependency requires two rebuild all's if they resolve at all and of course it was a nightmare to track down some of the problems. Other reflection systems for C++ require manual description in cpp files, not a horrible thing and this is the way I'm leaning once again, but not a single one I've found has the ability to be compiled out for a final release build and some of them add significant chunks of dead weight to the final product. An example, when working on PS2 game, a "properties" system for the editor added nearly 3 megs to an already huge (relatively speaking) executable, very bad news on a limited memory system.


At the same time, there are some reasons to keep "parts" of the system in place. All of the UI items such as descriptions, tool tips, editor types, etc can go away. But some things such as the properties and their types are very useful if you utilize a scripting system. You can kill two birds with one stone and integrate scripting at the same time you are describing the code to the editor. And of course, the root of the system, which I use is a RTTI implementation which I use to replace the limited C++ rtti abilities, so that gets to stay also. (NOTE: I say limited because I attach some additional attributes to describe the class for various reasons used in the engine which C++ rtti doesn't allow.)

Another item to consider, I don't want any non-stack allocation taking place before main enters and gets all the memory systems redirected and setup the way I want them (if at all possible of course). This is not a "critical" point but I'd very much like all the overhead of the RTTI tracked along with everything else. Since it exists from startup to shutdown, it is not a big issue, I just prefer to not miss anything if possible.

Ok, all the rambling explanation aside, here's the current thinking and initial working code examples:

1. Use an initializer chain based system. Even if there are memory allocations off of the stack they will be released prior to entering main. So the idea is basically:

static RttiInfo XXXXClassRttiInfo = RttiDef::Begin( "Someclass" )
.Property< uint32_t >( "Blargo" )
.Get< uint32_t >( &Someclass::Blargo )
.Set< uint32_t >( &Someclass::SetBlargo )
.Description( "Some uint32_t." )
.End()

It is verbose (can be wrapped with templates/macro's to simplify most of this to single lines) but the idea is intended to deal with all the items mentioned above. The stack based temporary which "Property" returns simply records the given information in local variables and the "End" puts those variables into the RttiDef which "Begin" put on the stack. In the case of "Description" in a retail build it just returns without doing anything and the string given it "should" be stripped as unreferenced by the linker.

2. The descriptive ability is easily extended in many ways depending on what the tools require/desire and things such as description just compile out to nothing in retail. Min/Max values for a control, type of editor to present, order of controls, groups of controls, whatever you may want.

3. With a bit of work all the remaining retail data can be serialized in the tools and saved with optimized level/area/whatever systems.

So, to do "everything" mentioned is a pretty large undertaking but to get what I want/need right now, I implemented just the editable property system in fairly short order and it compiles out of retail nicely. What I'm looking for here is alternatives, suggestions, etc which fit the desired requirements or are close enough to justify some hacking. A swift kick to the head for missing some library or alternative would be welcome. :)
Advertisement
Okay. Why?

What is your compelling reason to go this route? Why not derive from a class that specifies the calls and responses?

It isn't hard for a class to contain a bunch of data members that implement the things you mention. This does not require as much text manipulation and most of it can be done at zero cost with template parameters.

Even with your system you will need to pass around as much code to any system that uses it, so I'm just wondering why you are using that route (used by other languages) instead of a route more directly supported by the language.

Okay. Why?

What is your compelling reason to go this route? Why not derive from a class that specifies the calls and responses?

It isn't hard for a class to contain a bunch of data members that implement the things you mention. This does not require as much text manipulation and most of it can be done at zero cost with template parameters.

Even with your system you will need to pass around as much code to any system that uses it, so I'm just wondering why you are using that route (used by other languages) instead of a route more directly supported by the language.

Take the following as example. I.e.:


void MakeControls( ::Rtti* baseType )
{
const RttiInfo& info = baseType->TypeInfo();
const PropertyMap& propMap = info.Properties();

Property* baseProp = mpPropertyPanel->addGroup( info.Name() );

const PropertyMap::const_iterator begin = propMap.begin();
const PropertyMap::const_iterator end = propMap.end();
for( ; begin!=end; ++begin )
{
switch( begin->Type() )
..... Add properties for each type along the following lines ...
Property* prop = intManager.addProperty( begin->Name(), begin->Get< int >( baseType, begin->Name() );
switch( begin->ControlType() )
... use a proper control as defined in the rtti information ...
}
}


There is a fair amount of code missing in terms of maintaining a property panel and all the trivial UI bits such as if the item is for display only or can be edited, setting up the pretty names instead of the direct programmer name and tooltips etc etc. But in general, once something is described to the system the property panel can handle any variation. The core bit of the Rtti is nothing fancy, just a static structure for "IsA" to work within the object system itself and that stays even in retail, almost everything which the property panel above uses would be stripped out of the final executable though. All the strings go away as they are only used in the editor, and just as an additional benefit, you can serialize all of this to Xml for debugging and documentation reasons if that is of interest.

So, is the separation compelling or not? I like the locality of the description bits to the code bits personally, I'm lazy and hunting down xml files and updating descriptions in those or running preprocessing tools etc annoys me. So, having all the description right in the source usually benefits things, also many silly errors are quickly caught through compile time asserts and such. Anyway, my reasoning is that I have liked working with similar systems in the past (when they worked) because they are minimally intrusive on my general programming work and they usually end up supplying a superior and consistent editor solution. From what I believe you're description is referring to, I don't think their is any real difference, just some naming/location differences in the code?
In my personal opinion, RTTI and reflection is evil :P
The major drawback of it is, that you transfer compile time errors to run-time errors. It is often very easy to write up some code really quickly, but after some time you are encountered with run-time errors, in worst case reported by some user with almost none information about the nature of the error, and you will have a really hard time to debug it.
The real time sink behind developing a working program is not writing code but debugging and maintaining it.

In my personal opinion, RTTI and reflection is evil :P
The major drawback of it is, that you transfer compile time errors to run-time errors. It is often very easy to write up some code really quickly, but after some time you are encountered with run-time errors, in worst case reported by some user with almost none information about the nature of the error, and you will have a really hard time to debug it.
The real time sink behind developing a working program is not writing code but debugging and maintaining it.

I don't disagree in general but I have shipped several games with reflection based systems which turned out not having the runtime problems you suggest. Having said that though, I'm not suggesting I want to use the reflection for the game in this case, but primarily only as runtime binding with the editor. The little bit of rtti which would not compile out is simply for IsA/HasA/TypeId query information which I've used in code for years without any problems. Anyway, as the old saying goes, the last 10% of any game is 90% of the work, I'm definitely all about reducing that 90% down as much as possible by catching as much at compile time as possible and removing as many of the error prone items as possible. In the case of the edit bindings though, I'm open to better suggestions.

This topic is closed to new replies.

Advertisement