Hi everyone,
Lately I've been working on a reflection system for my C++ game engine. It's been a fun process because it really forces you to explore the language, and I think its a good choice because it will have several benefits to both gameplay programming (determining if a given GameObject is downcastable or implements an interface) and system programming (automatic serialization, automatic GUI behaviour, etc). Despite some of the options for having a build system generate the reflection information automatically, I've decided to make the user register their types by hand just because its much less error prone and gives a finer degree of control. I've come up with two models for how the reflection information can be registered, but I need some advice on deciding which one. Please note that this is just how information is registered, I've already figured out much of how it will be retrieved (including handling for polymorphism).
At the moment, reflection types are cut up into the following classes:
Type (abstract)
PrimitiveType (extends Type)
PointerType (extends PrimitiveType)
ClassType (extends Type)
TemplateType (extends ClassType)
InterfaceType (extends Type)
Module (collection of ClassTypes and InterfaceTypes)
Primitive types and pointer types do not need to be registered, they are generated through template specialization of the TypeInfo<T>() function (which is the main point through which type information is retreived).
Theres also the class "Object", which is extended by any types that want to make use of first-class support for reflection (such as GameObject).
Registration Model A)
Reflection information is part of the class interface. Adding the REFLECTABLE macro to a class header would add the private static get_type_information() function. The user would then use several more macros in the class source file to define which fields and attributes of the class are reflected.
Pros: Elegant. If a class is moved to a different module, the reflection information naturally moves with it. Finding where the registration occurred is extremely easy. It also solves the issue of reflecting templated classes, as the reflection info is generated by the compiler along with the rest of the class when it's instantiated.
Cons: Not as clean. Classes that otherwise should not know about the existence of a reflection system are now strongly linked to it.
Registration Model B)
Classes are defined normally, then reflection information is registered for every class in the module at once in a module-wide registration function.
Pros: Doesn't clutter the rest of the code, and serves itself well towards automatic generation if I do decide to pursue that in the future (since all reflection information for a module would exist in one file).
Cons: Creates sort of a chicken and egg issue with registration. If a class references a type (as a base or field) that has not been registered yet (even if it appears later in the same registration) then it will crash. This issue does not occur with Model A, as types are registered on demand and cycles do not occur. I could have a system where each class in the module must be "declared" with the system before any of them are registered, but that's just not as nice. Also, while reflecting templated classes is still possible (via template specialization), its just not as clean.
Anyway, both systems accomplish the same set of goals I laid out to begin with. They both lend themselves to having a type database where types can be looked up. At the moment I'm leaning a bit more towards solution A, but I'd like to hear your guy's thoughts. Which one is the better solution? Are there other options I'm not thinking of?
Thanks