Instantiate class from name?

Solution 1:

This is a problem which is commonly solved using the Registry Pattern:

This is the situation that the Registry Pattern describes:

Objects need to contact another object, knowing only the object’s name or the name of the service it provides, but not how to contact it. Provide a service that takes the name of an object, service or role and returns a remote proxy that encapsulates the knowledge of how to contact the named object.

It’s the same basic publish/find model that forms the basis of a Service Oriented Architecture (SOA) and for the services layer in OSGi.

You implement a registry normally using a singleton object, the singleton object is informed at compile time or at startup time the names of the objects, and the way to construct them. Then you can use it to create the object on demand.

For example:

template<class T>
class Registry
{
    typedef boost::function0<T *> Creator;
    typedef std::map<std::string, Creator> Creators;
    Creators _creators;

  public:
    void register(const std::string &className, const Creator &creator);
    T *create(const std::string &className);
}

You register the names of the objects and the creation functions like so:

Registry<I> registry;
registry.register("MyClass", &MyClass::Creator);

std::auto_ptr<T> myT(registry.create("MyClass"));

We might then simplify this with clever macros to enable it to be done at compile time. ATL uses the Registry Pattern for CoClasses which can be created at runtime by name - the registration is as simple as using something like the following code:

OBJECT_ENTRY_AUTO(someClassID, SomeClassName);

This macro is placed in your header file somewhere, magic causes it to be registered with the singleton at the time the COM server is started.

Solution 2:

A way to implement this is hard-coding a mapping from class 'names' to a factory function. Templates may make the code shorter. The STL may make the coding easier.

#include "BaseObject.h"
#include "CommonClasses.h"

template< typename T > BaseObject* fCreate( int param1, bool param2 ) {
    return new T( param1, param2 );
}

typedef BaseObject* (*tConstructor)( int param1, bool param2 );
struct Mapping { string classname; tConstructor constructor; 
    pair<string,tConstructor> makepair()const { 
        return make_pair( classname, constructor ); 
    }
} mapping[] = 
{ { "class1", &fCreate<Class1> }
, { "class2", &fCreate<Class2> }
// , ...
};

map< string, constructor > constructors;
transform( mapping, mapping+_countof(mapping), 
    inserter( constructors, constructors.begin() ), 
    mem_fun_ref( &Mapping::makepair ) );

EDIT -- upon general request :) a little rework to make things look smoother (credits to Stone Free who didn't probably want to add an answer himself)

typedef BaseObject* (*tConstructor)( int param1, bool param2 );
struct Mapping { 
    string classname; 
    tConstructor constructor; 

    operator pair<string,tConstructor> () const { 
        return make_pair( classname, constructor ); 
    }
} mapping[] = 
{ { "class1", &fCreate<Class1> }
, { "class2", &fCreate<Class2> }
// , ...
};

static const map< string, constructor > constructors( 
      begin(mapping), end(mapping) ); // added a flavor of C++0x, too.