A C++ Class Factory for JUCE

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:

class Creator {
public:
   virtual Product* Create(ProductId id);   
};


Product* Creator::Create(ProductId id) {
   if (id == MINE)   return new MyProduct;
   if (id == YOURS)  return new YourProduct;
   // repeat for remaining products...
   
   
   return 0;
   
}

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:

   class Base
   {
   public:
      static Base* Create(String typeName);
   };
   
   // ...and later in app code, 
   Base* derived1 = Base::Create("MyDerivedType");
   Base* derived2 = Base::Create("SomeOtherDerivedType");   

…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:

   template <class Base>
   class BaseFactory
   {
   public:
      // register this factory with the base class when we create it.
      BaseFactory(String typeName)
      {
         Base::RegisterFactory(typeName, this);
      }

      // create and return a pointer to our Base class type. 
      virtual Base* Create() = 0;
   };

If our base class contains a static HashMap like

   static HashMap<String, BaseFactory<Base>* > fFactoryDb;

then things are simple, right?

   class Base
   {
   public:
      static RegisterFactory(String  typeName, BaseFactory<Base>* factory)
      {
         fFactoryDb.set(name, factory);
      }

      static Create(String typeName)
      {
         BaseFactory<Base>* factory = fFactoryDb[typeName];
         if (factory)
         {
            return factory->Create();
         }
         // we don't know about the requested typename 
         return nullptr;
      }
   };

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:

template <class Base, class Derived>
class Factory: public BaseFactory<Base>
{
public:
   Base* Create() override
   {
      return new Derived();
   }
};

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:

static Factory<Base, Foo> fooFactory("foo");

…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:
template <class T>
class FactoryDatabase
{
public:
   FactoryDatabase()
   {
      // Nifty counter init -- the first time a static instance 
      // of this class happens to be created, create the HashMap that we use
      // so it's ready for factories to be registered.
      if (0 == fCount++)
      {
         fDatabase = new HashMap<String, BaseFactory<T>* >();
      }
   }
   ~FactoryDatabase()
   {
      // ...and nifty destruction. The last time a static instance of us is
      // destroyed at shutdown, only then delete the HashMap.
      if (0 == --fCount)
      {
         delete fDatabase;
         fDatabase = nullptr;
      }
   }

   /**
    * Register a Node Factory object with a name that can be used to create 
    * Node objects at run time. 
    * @param name    name of the class to use when creating one 
    *                (not necessarily the same as the actual C++ class name)
    * @param factory Pointer to a NodeFactory object that will return an instance
    *                of the Node class.
    */
   static void RegisterFactory(StringRef name, BaseFactory<T>* factory)
   {
      jassert(fDatabase);
      DBG("Registering factory for " << name);
      fDatabase->set(name, factory);
   }   
   /**
    * Create a Node object using its name.
    * @param  name Typename of the class you'd like to create.
    * @return      A pointer to the object if we have a registered factory
    *              object that knows how to make it, or `nullptr`.
    */    
   static T* Create(StringRef name)
   {
      T* retval = nullptr;
      BaseFactory<T>* factory = (*fDatabase)[name];
      if (factory)
      {
         retval = factory->Create();
      }
      return retval;
   }

protected:
   static int fCount;
   static HashMap<String, BaseFactory<T>* >* fDatabase;
};   

…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:

#include "factoryDatabase.h"

class Base;

using BaseFactoryDb = HashMap<String, BaseFactory<Base>*>;
using BaseFactoryDatabase = FactoryDatabase<Base>;

static BaseFactoryDatabase bfd;

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

#include "baseFactoryDatabase.h"


// This source file only exists as a place to park the storage for the
// two static data fields used in the BaseFactoryDatabase class.

template<> int BaseFactoryDatabase::fCount = 0;
template<> BaseFactoryDb* BaseFactoryDatabase::fDatabase = nullptr;

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:

namespace
{
   // here in an anonymous namespace, we create an instance of the 
   // appropriate Factory type for each of the derived classes we wish to 
   // be able to create through the `Base::Create("someType")` interface. 
   // Declaring these has as a side effect the registration of the factory 
   // classes in the factory database object, so there's nothing else to do
   // for these factories to be usable. 
   Factory<Base, Unity>    unityFactory("unity");
   Factory<Base, Smaller>  smallerFactory("smaller");
   Factory<Base, Bigger>   biggerFactory("bigger");
};

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:

/**
* Perform some sort of arithmetic operation.
* @param  input input value  .
* @return       Output value. 
*/
virtual int SomeIntOperation(int input) = 0;

/**
* Perform some sort of operation on a string. 
* @param  s Input string.
* @return   Output string. 
*/
virtual String SomeStringOperation(const String& s) = 0;

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.
void runTest() override
{
   beginTest("object creation");
   // things that should be created...
   ScopedPointer<Base> unity = Base::Create("unity");
   expect(nullptr != unity);

   ScopedPointer<Base> smaller = Base::Create("smaller");
   expect(nullptr != smaller);

   ScopedPointer<Base> bigger = Base::Create("bigger");
   expect(nullptr != bigger);

   // we don't know about the imaginary type, so we should get
   // a NULL Pointer back from the Create() call.
   ScopedPointer<Base> notThere = Base::Create("imaginary");
   expect(nullptr == notThere);


   beginTest("Integer operations");
   // Verify that the 3 objects we've created are of the 
   // correct type by exercising their API.
   expect(100 == unity->SomeIntOperation(100));
   expect(99 == smaller->SomeIntOperation(100));
   expect(101 == bigger->SomeIntOperation(100));

   beginTest("String operations");
   const String kTestString("ThIs Is A dIfFeReNt StRiNg");
   expect(kTestString == unity->SomeStringOperation(kTestString));
   expect(kTestString.toLowerCase() == 
      smaller->SomeStringOperation(kTestString));
   expect(kTestString.toUpperCase() == 
      bigger->SomeStringOperation(kTestString));
}

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

Chief Engineer, Development Practices 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. Also creator of @tmbotg. Day job @artandlogic
RT @JazzTrombonist: I accidentally texted my wife with voice recognition...while playing the trombone https://t.co/tWCPSXbbrO - 1 hour ago
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.