Base factory method with automatic registration

Started by
8 comments, last by kdmiller3 13 years, 2 months ago
I'm trying to create a factory method for a base class which instantiates a derived class corresponding to the method's parameters.
So I can do:
Base* b = Base::create("DerivedClass");
I read this interesting post on Stackoverflow http://stackoverflow...c/534396#534396 (by epatel) but noticed he uses a factory singleton (Factory::instance()->registerCreator()).
Instead of a singleton I used a static varible. Here is my solution:

(C++)

#include <iostream>
#include <string>
#include <map>
using namespace std;

class Base
{
public:
typedef Base* (*CreateFun)();

static Base* create(string name)
{
FunctionMap::iterator it = creators.find(name);
if (it == creators.end())
return NULL;
return (it->second)();
}
static bool reg(string name, CreateFun fun)
{
creators[name] = fun;
return true;
}
virtual ~Base() {}

private:
typedef map<string, CreateFun> FunctionMap;
static FunctionMap creators;
};

Base::FunctionMap Base::creators;

// this registers a derived class in the factory method of the base class
// it adds a factory function named create_NAME()
// and calls Base::reg() by the help of a dummy static variable to register the function
#define REGISTER( _name ) \
namespace { \
Base* create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = Base::reg(# _name, create_ ## _name); }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class DerivA: public Base
{
public:
DerivA()
{
cout << "DerivA was created" << endl;
}
};

REGISTER(DerivA)

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class DerivB: public Base
{
public:
DerivB()
{
cout << "DerivB was created" << endl;
}
};

REGISTER(DerivB)

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int main()
{
Base* b = Base::create("DerivA");
b = Base::create("DerivB");
return 0;
}

When you create a new derived class, all you need to do is to use the REGISTER() macro.
Note I don't have a separate factory class. I use the base class for this.
Note also that this code should be slightly modified if split into multiple files (multiple compilation units: no guarantee on the order of initialization of static variables).
Actually I want to use this as a component factory, so I can do something like

GameObject obj;
for each XML component node {
Component* comp = Component::createFromXml(node).
obj.addComponent( comp );
}

What do you think of this solution?

EDIT: having problems with identation
Advertisement

What do you think of this solution?


I think that using static for anything stateful in C++ is a needless overcomplication. Between undefined behavior, coupling of everything from pre-processor down to linker and exposing more language quirks than just about any other concept it's simply a brittle approach and a false optimization - it simply doesn't save anything in the long run and has far-reaching implications on build system (which is what should be really optimized).

IMHO:// factory.h
class Factory {
Entity * make(...);
}

// factory.cpp
#include <foo.h>
#include <bar.h>
#include <baz.h>

Factory::Factory() {
register_type(foo);
register_type(...);
...
}

// main.cpp
int main() {
Factory factory;
}
// or
Factory * factory;
int main() {
factory = new Factory();
}

As new classes are added or removed, factory alone needs to be recompiled. All registrations are in one place, and the number of lines of code that must be written is *exactly* the same as with various "auto" registration libraries, one line per type.

C++ is static language, so all types are known either at compile time or loaded via exported function from a DLL, the function itself is identical for all types. There is no real benefit towards dynamic registration.

Or, add self-registering handlers to some specific directory, when building, list contents of that directory and construct a .h/.cpp file using sed/awk/perl/... like above. It allows automatic registration while remaining as simple as possible without praying for stars to align so that auto-magic works as intended.
I agree your code is simpler. But when you add a new class you don't have everything in one place, you have to search for the factory code and add it there.
Actually there are 2 lines to add: #include "foo.h" and register_type(foo) :D

Thanks for your opinion. I guess I will stick to your method, since it's simpler to understand.

How do others handle this problem?
With a big ol' function that registers all the registration-needing stuff.

Automatic registration through global/static variables is fragile and bug-prone. It solves a problem which is not a large problem, by introducing a solution which IS a large problem.
I've used a similar automatic-registration technique in production code for years, though using the constructor of meta-class objects that also store shared instance properties. There's only one static method ("Find") and no macro trickery, just a set of "prototype" meta-class instances that seed the class system database at static initialization time. That doesn't mean it's right, though. :)

As Antheus suggests, this relies on compiler- and setting-specific behavior regarding unreferenced object instances. For example, the Visual C++ compiler will remove unreferenced static and const (which are implicitly static) instances since it knows nothing outside the compilation unit can access them. It won't removed unreferenced globals even with whole-program optimization, though there's no absolute guarantee that won't change in the future. According to MSDN, the linker includes or excludes whole COMDATs, and the compiler will package data into a COMDAT only if it's marked __declspec(selectany).

Aside from that, you can't rely on any particular order for static initializations. Specifically, there's no guarantee that your database has been initialized by the time a class attempts to register itself. You might get away with it for a while, but you'll eventually experience the infamous "static initialization order fiasco" first-hand. One solution is the "construct on first use" idiom:

FunctionMap &GetCreatorMap(void)
{
static FunctionMap creators;
return creators;
}
Thank you for your opinions!

As Antheus suggests, this relies on compiler- and setting-specific behavior regarding unreferenced object instances.

Can you explain that a bit more? Is this really undefined behavior or are the compilers not compliant?
If you take care I don't see any reason for it not working and being portable.
Of course, I agree, you shouldn't take the high risk...


Aside from that, you can't rely on any particular order for static initializations. Specifically, there's no guarantee that your database has been initialized by the time a class attempts to register itself. You might get away with it for a while, but you'll eventually experience the infamous "static initialization order fiasco" first-hand. One solution is the "construct on first use" idiom:

FunctionMap &GetCreatorMap(void)
{
static FunctionMap creators;
return creators;
}

[/quote]
Yes, I noticed that, thanks. (I wrote that the code should be slightly modified if split into multiple files)
Basically, the C++ standard only guarantees that a global will be initialized if the global is actually used. If you don't use it, the compiler doesn't have to initialize it.
"The difference between theory and practice is that in theory there is no difference between theory and practice and in practice there is." :)

Unless you plan to support an unusual variety of platforms and compilers or require strict language standards compliance, you're not likely to run into too many problems even though the behavior is undefined. In practice, I've found that the static initialization approach has worked fine with Visual C++ 5.0, 6.0, 2002, 2003, 2005, 2008, and 2010 on PC, as well as Visual C++ for Xbox, ProDG for Playstation 2, and CodeWarrior for GameCube. I can't promise it will work on other platforms, but would be genuinely surprised if it didn't.

On the plus side, if it doesn't work, it should consistently not work. At that point, you can fall back to registering your classes explicitly.

In practice, I've found that the static initialization approach has worked fine with Visual C++ 5.0, 6.0, 2002, 2003, 2005, 2008, and 2010...

...unless you put the code in a static library in which case MSVC will consistently strip it out.

...unless you put the code in a static library in which case MSVC will consistently strip it out.


It's definitely another gotcha to watch out for, but makes perfect sense when you think about it. A static library just a collection of things the compiler can import if it wants. It doesn't have much of an identity of its own.

Dynamic link libraries, on the other hand, have their own global construction phase when loaded. I used that feature to good effect on my side project, though I had to add the corresponding "unregister" machinery to handle the global destruction phase when unloaded. (I got burned by that one the first time I restarted a level with an associated DLL. The component registry was full of references to now-unloaded objects, and hilarity ensued.)

This topic is closed to new replies.

Advertisement