A C++ Class Factory for JUCE

The Problem

So, I’m working on a side project (as one does), and reach the point in development where I need to be able to take a tree of objects that all share a common base class and persist them to and from disk.

I prefer using plain text files to binary (for a bunch of different reasons, most of them enumerated in the original The Pragmatic Programmer book), so the problem really boils down to:

At runtime, I need to be able to convert a string containing the name of a class into a pointer to an object of the corresponding C++ class.

If you do like I did, and go to the shelf to see what the old Gang of Four Design Patterns book has to say on the matter, maybe you’d have been underwhelmed, too.

If you turn to their chapter on the Factory Method, their example code looks like this:

Obviously, this code will work.

There are no doubt an endless number of instances of this exact approach found in code that’s been working reliably for decades, but some things about the design don’t resonate cleanly with me:

  • I’m not crazy about the maintenance required of keeping a list of classes in a central place
  • Similarly, I don’t like having this separate other Creator object, especially in the likely scenario where there will be multiple types within the project that will need this method of instantiation.

My preference would be that given a Base class named Base, I’d be able to have code like this:

…with a minimum of letting knowledge of those derived classes leak around the system; in my use in this project, the IS-A relationship is strict, so any operations that are performed on any of those derived classes need to be able to be performed through a pointer-to- Base.

Wherever possible, the base & derived classes will do whatever needs to be done so that all of the plumbing connecting things together so that all these pieces work is handled automatically behind the scenes.

Aside: The code that I’ve written here comes from a project that’s using the JUCE C++ application framework, so I’m using its container classes like Array and HashMap. There’s nothing about the technique being used here that requires JUCE in any way; it would be simple and straightforward to swap in (e.g.) std::vector and std::map containers and use this design in a completely vanilla C++ project.

The Factory Class

The first piece that we’ll build out is the actual Factory class that’s able to create an instance of some class derived from a base class that supports this system (more on that later).

Some of you (probably with either a small amount of C++ experience or a very large amount of C++ experience) will think I’m unbalanced for saying this, but templates to the rescue.

At its simplest, the factory wants to look something like this:

If our base class contains a static HashMap like

then things are simple, right?

The actual factory class needs to be derived from BaseFactory<Base> to additionally be templated on the type of the class that’s being created by that concrete instance of the factory, something like:

Then, if we assume as an example, a concrete class Foo that’s derived from Base, all we need to do to make everything work is add in the Foo.cpp file a static instance of this factory class, like:

…and we should be able to create new instances of Foo by calling Base::Create("foo");

Complication: Everything Goes Boom

So at this point we add enough test code to try things out. Add a derived class and a factory for it, and things run just fine. Add another derived class, recompile and run the app and BOOM we’re in the debugger.

The problem: we have that static HashMap that associates type names with factory functions, and static instances of the factory functions that, as part of their creation, insert pointers to themselves in that static HashMap.

C++ makes no guarantees regarding the order of initialization of static variables like this. In our first test, we were lucky that the HashMap was created before we tried to use it. After that, our luck ran out.

Some Folks Call it a Kaiser Blade

At some point in the last 20 years I had read about the solution to this problem, but it took a bit of Google and StackOverflow to remember it fully: a Nifty Counter (some folks call it a “Schwarz Counter”). You can (and should!) spend some time reading up on this from more scholarly sources than me (there are a variety of approaches involving various tradeoffs), but the short version of this idiom is that we protect this shared static object with a static reference counter that ensures the object will be correctly initialized before its first use as well as properly destroyed after the last possible use of it.

Factory Database

To make this work, we create a new template base class FactoryDatabase that:

  • Handles the Nifty Counter mechanics
  • Contains the HashMap associating type names with Factory objects.
  • Exposes API functions to register Factory objects, and to create new objects of classes derived from the template base type:

…and then for any classes that you’d like to create through this system, you need a new header & cpp file (in the demo app it’s baseFactoryDatabase.h/cpp) that declares a static instance of the FactoryDatabase:

…and in the cpp file we need to have the actual definition of the two static members in the FactoryDatabase:

Derived Classes

The only remaining piece to make things work as intended is to declare a Factory object of static duration for each of the classes derived from the base that should be factory buildable. In the demo app here, we create three Derived classes, Unity, Smaller, and Bigger. In production code, I’d expect these to each be in separate source files, but given their trivial nature, they share a single source file, and their factories are all declared together as well:

The Demo App

As a simple demo, we have a console application that instantiates objects, runs unit tests, and exits.

The Base class exposes this API:

and we define three derived classes that implement this API:

  • Unity returns its arguments without modification
  • Smaller returns a smaller version of its arguments (subtracting 1 in the IntOperation, converting the String srgument to lowercase)
  • Bigger makes its arguments bigger (adding 1, converting to uppercase)

The unit tests try to make sure that things are working, where ‘working’ is defined as:

  • Calling Base::Create() with a typename of a registered class creates an object.
  • That object is of the correct C++ class
  • Calling Base::Create() with an unknown typename does not create an object.

Source

Source code for this JuceClassFactory system is available on github. It’s licensed under the MIT license; if you find it useful, use it for what you will, and let me know.

Image via me on Flickr

Brett g Porter

Brett g Porter

Lead Engineer, Audio+Music at Art+Logic
Lead Engineer, Audio+Music development at Art+Logic. Always looking for excuses to write code. Tweets at both @artandlogic and @bgporter.
Brett g Porter

@bgporter

Music+Software+Music Software+Ice Cream. Relapsing composer/trombonist. Day job @artandlogic. Creator of @tmbotg.
RT @WesFlinn: Playing again has made me a better composer, better teacher, and better person. If you can at all, make music today. - 18 hours ago
Brett g Porter
Brett g Porter

Latest posts by Brett g Porter (see all)

Tags:

Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.