Journal #7

Published November 28, 2016
Advertisement

I hope that everyone (at least in the US) had a good holiday. I took the week off from work and while I couldn't devote the entire time to working on my hobby code I was able to get a nice chunk of something done.

First I took some code that I had written for my component system and generalized it a bit so that I could reuse it. I call it an 'instantiator' and it's designed to do two things. The first is to act as an object pool, managing resource allocations and memory reuse as instances are needed and discarded. The second function is to manage some super-simple type information to allow for fast and safe type checking and casting. The instantiator comes in two flavors 'virtual' and 'concrete', the virtual instantiator just does the type stuff and acts as a kind of place holder. It also acts as the base instantiator type, defines a static public interface that the client systems go through and defines the private virtual interface that the concrete instantiators implement to do all the allocation stuff. Lastly the virtual instantiator defines a static array of instantiator pointers that it uses to route all the static API calls to the instantiator for the desired class. The concrete instantiator inherits from the virtual one picking up the type information support and implementing the virtual memory management interface. Now to support all this, the classes using the instantiator (which relies heavily on templates) have a few required members to them for things like class name strings and other static members. I've setup macros to simplify a lot of these things, but really all's that left to do is declare a type and then declare an instance of either a virtual or concrete instantiator for that type in a cpp file at file scope. The constructor for instantiator then registers itself (during pre-main construction) into the static array of instantiators. The basic gist of the implementation looks like this:class virtual_instantiator{private: // a few class identification members std::vector parenting_types; static virtual_instantiator *set[ MAX_TYPES ]; virtual type* alloc_internal( ); virtual void free_internal( type* ); // other various system virtual callspublic: static type* alloc( ); static void free( type* ); static bool is_type_of( is_type, of_type ); // static versions of the other virtual system calls virtual_instantiator( type, parent_type, classname );};class concrete_instantiator : public virtual_instantiator{private: // memory allocation stuffprotected: // implementations of virtual interfacespublic: concrete_instantiator( );};
This obviously doesn't compile. I've attached the full header to this journal entry if you're interested.

During system initialization (inside main) a static function is called for the virtual_instantiator which then calls the initialization function for all the instantiator instances. One of the primary things that happens here is filling in the parenting_types vector of bools. This basically makes a copy of the bools from the instantiator for the parent type and then sets the bool for the current type. Then anytime that I want to know if one type is part of another's tree it's just a bit check. As part of the instance allocation, I also store the most derived type into a variable of the instance so that I know what it was allocated as.

Some limitations: it only supports single inheritance and it's currently limited to an unsigned short (65535) worth of different types. Other than that the tree of classes can be as deep or as wide as I want it to be. The single inheritance limitation doesn't bother me personally and 65K of types seems more than enough for one game (since any game specific type ids could be used for different types in different projects).

Now, I had originally written this whole setup pretty specifically for the creation and management of components and it was working quite well. But I decided that I would also like it to manage my data driven type definitions, things like class or weapon stats. So I refactored what I already had a bit (mostly adding more templates) so that my components and data definitions used the same code but used different instances of the static instantiator array. This refactored version is what is attached to this journal. I was also pleased that for the most part this worked out relatively painlessly. I did have to do some looking up of more recent C++ template features to get a couple things working the way I wanted in other headers but learning that stuff is one of the reasons I want to make an effort on this hobby code.

The main effort of my holiday coding was actually writing up the code for my data definitions types. This code consists of two parts, the data_definitions base class and a class I named definition_library. The data_definition class was mostly a bit of copy/paste from the component class; all the bits and pieces that are required to interact with the instantiator and the macro for declaring derived types. The definition_library wraps up access to the instantiator (forcing an additional construction requirement), handles parsing of certain json files into data_definition instances and manages a mapping of string names to the data definitions (the name being the additional construction requirement). The main thing I decided I wanted to really enforce is that when accessing any of the definitions that have already been created, was that they should be const. If you create an instance at runtime, the creation interface does return a non-const instance, but once you let that go and get it by name through the library, the only way to do it will give you const data. My reasoning behind this is that the definition structures are intended as application-static data. Once the game is running and has loaded up that data it should be treated as unchanging. Now for purposes of development it probably makes sense that I can tweak some of those numbers at runtime. For now, that task has been relegated to my todo list. I'll always have the option of making type specific console commands for the definitions that I specifically want to be able to tweak, but I think I'd like to find a way to leverage my reflection system to write a single console command as part of the definition library that can modify any member of any type.

Lastly, once I had all that setup I built my initial test case of a type that actually derived from data_definition and had instances that were created when a file was loaded. Continuing the theme of game design I'm working through, the data definition was the start of information about ship classes. Right now it just has a hull class (cruiser, battleship, etc) and an optional modifier (command, missile, escort).

Happy to answer any questions about anything in this writeup. I know I probably touched nerves of a few people mentioning components and/or reflection without going into a whole lot of detail. I do plan to do a journal about them at some point, so you can look forward to that. Probably save that for a week when I don't have a chance to write much code or make a major addition to them.

Previous Entry Journal #6: Kryptonite
Next Entry Journal #8
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement