December 22, 2013

C++ 11 and Types

I've been trying to ramp up on all the language changes as part of C++ 11, and have been trying to figure out how to do something similar to XNA's Content.Load<T> as part of it, and I'm a bit stymied so far.  Let me explain.

XNA's ContentManager has a ton of features that seem like they'd work wonderfully in C++'s RAII world.  When you call .Load<T>, it first checks to see if the content has already been loaded through this particular ContentManager.  If it has, you get the cached copy back.  Otherwise, it tries to load the file, instantiates the proper type loader, caches the loaded object, then returns a reference to the object.  When ContentManager is disposed of, it handles cleaning up all of its cached objects for you.  Plus, I can keep multiple ContentManagers around: one for "common" items (characters, UX elements, etc.), and one per "level" (level-specific textures and geometry).  That way, you just dispose the level-specific content manager and start a new one up for a new level.  This pattern just screams RAII to me.  The gotcha is that each ContentManager can house multiple types.

Now at least at the moment, I'm at a loss for how to do this in a type-safe, RAII friendly manner in C++.  It's easy if I create a ContentManager per type of content to load into it (ContentManager<Texture2D>, ContentManager<LevelData>, ContentManager<BlahBlahBlah>, etc.), but then I lose the ease of working with a single object.  I could add a strongly typed reference for each type into ContentManager (ContentManager.LoadTexture2D, ContentManager.LoadLevelData, etc.), but then I lose loose coupling.  I could just keep a hash_map<string, void*> and force dynamic_cast to be used on load, but then I lose RAII cleanup of each subobject.

I'm sure there is a graceful solution to this that I just haven't found yet, but it's a fun problem space to explore.

Minor update: This weekend has taught me that I have much more to learn about the wonderful world of shared_ptr<T>.  The last major C++ codebase I worked in was a barely C++'d C codebase, so my mental model of C++ is still skewed horribly.

5 comments:

Hussein Farran said...

Hi,

Have you explored the possibility of using templates to enable ContentManager.Load to take in type arguments and handle multiple types?

Michael Russell said...

Hussein,

Indeed I have, but templates compile out to a separate template per type as far as I can tell. Is it possible to declare a template at the method level instead of the class level?

Michael Russell said...

And more importantly, how would templating fix the RAII dispose issue with a single parent object?

Hussein Farran said...

Sorry about the delay, I was doing a bit of research to hopefully understand this better.

From what I've learned it seems you can use a shared_ptr to keep track of each object and allow for RAII disposal when the ContentManager goes out of scope. Templates can indeed be applied to methods.

I consulted a friend, who is far more experienced with C++ in general than I am, and he cooked up this example: http://coliru.stacked-crooked.com/a/645d31d8782b96f4

I didn't quite understand your question in your second comment, but hopefully this possible solution might cover it by chance.

Michael Russell said...

Hussein,

Thanks for the example. Again, trying to ramp up on C++11 is taking some time, but I appreciate the help.