MetaData.hpp - Header for the C++ introspection class
So what does this class do?
- This class stores basic runtime type information about a C++ type (built-in or user-defined). Information is gathered at compile time (when the compiler knows all there is to know about a type) and is stored for use at runtime. This works with template meta-programming, where I have the C++ compiler instantiate functions that perform the actions I'm requesting (like copy constructing a class). There is also some game specific information stored here (IsSpace(), IsComponent(), etc). Basically, this class stores any information that can be useful for python or level editors to use.
In this class you can see a few things about my coding style:
1) I like to organize code in namespaces (or modules for languages that have them)
2) I generally wrap access of a class's components inside of the interface to that class
Why do I do those things?
- I use namespaces in C++ mostly for the auto-completion. :) It makes it easy for a dev using my code to get a list of all of the available methods/types pertaining to a particular object. Ideally, I only have data and operators on the object and all of it's methods are stored in a related namespace, C-style. This makes it easier to parallelize code and stick to only public interfaces.
- I generally wrap any call through code inside of the class interface (so meta->prop_holder->members becomes meta->GetMemberProperties()) because this allows me to change how access to the class's internals work without the user having to change their code. In general, I think that's good API design.
What could I have done better?
- I could have wrapped the internals of this class into either a pimpl or made the metaclass an interface. Then the internals of the class wouldn't have been visible outside of the class. The reason I didn't do this was because it would have made some initialization logic even more complicated (since I derive another class from MetaData). I could also argue that the extra indirection could have been costly since this class is ubiquitous in our engine, but without profiling, I'd honestly just be talking out of my... yep.
- I could have made a bit flag for all of the bool variables. This would drastically cut down on the size of the MetaData object. It honestly wouldn't have been hard to make this memory optimization, with an enum and a macro/function the code would've practically written itself, but I knew that there was only a limited number of MetaData objects that could exist in our engine (one for every C++ type) and there were other things to write.
- I could've made it so that meta only deals with public information and separated serialization (which probably needs access to private data) into a separate function. This would've allowed meta to not have to be a member a friend of the object I was binding and the GetProp/GetFunc/GetData() functions would have been a lot safer.
- This class stores basic runtime type information about a C++ type (built-in or user-defined). Information is gathered at compile time (when the compiler knows all there is to know about a type) and is stored for use at runtime. This works with template meta-programming, where I have the C++ compiler instantiate functions that perform the actions I'm requesting (like copy constructing a class). There is also some game specific information stored here (IsSpace(), IsComponent(), etc). Basically, this class stores any information that can be useful for python or level editors to use.
In this class you can see a few things about my coding style:
1) I like to organize code in namespaces (or modules for languages that have them)
2) I generally wrap access of a class's components inside of the interface to that class
Why do I do those things?
- I use namespaces in C++ mostly for the auto-completion. :) It makes it easy for a dev using my code to get a list of all of the available methods/types pertaining to a particular object. Ideally, I only have data and operators on the object and all of it's methods are stored in a related namespace, C-style. This makes it easier to parallelize code and stick to only public interfaces.
- I generally wrap any call through code inside of the class interface (so meta->prop_holder->members becomes meta->GetMemberProperties()) because this allows me to change how access to the class's internals work without the user having to change their code. In general, I think that's good API design.
What could I have done better?
- I could have wrapped the internals of this class into either a pimpl or made the metaclass an interface. Then the internals of the class wouldn't have been visible outside of the class. The reason I didn't do this was because it would have made some initialization logic even more complicated (since I derive another class from MetaData). I could also argue that the extra indirection could have been costly since this class is ubiquitous in our engine, but without profiling, I'd honestly just be talking out of my... yep.
- I could have made a bit flag for all of the bool variables. This would drastically cut down on the size of the MetaData object. It honestly wouldn't have been hard to make this memory optimization, with an enum and a macro/function the code would've practically written itself, but I knew that there was only a limited number of MetaData objects that could exist in our engine (one for every C++ type) and there were other things to write.
- I could've made it so that meta only deals with public information and separated serialization (which probably needs access to private data) into a separate function. This would've allowed meta to not have to be a member a friend of the object I was binding and the GetProp/GetFunc/GetData() functions would have been a lot safer.