Is there a way to instantiate objects from a string holding their class name?
No there isn't. My preferred solution to this problem is to create a dictionary which maps name to creation method. Classes that want to be created like this then register a creation method with the dictionary. This is discussed in some detail in the GoF patterns book.
Nope, there is none, unless you do the mapping yourself. C++ has no mechanism to create objects whose types are determined at runtime. You can use a map to do that mapping yourself, though:
template<typename T> Base * createInstance() { return new T; }
typedef std::map<std::string, Base*(*)()> map_type;
map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;
And then you can do
return map[some_string]();
Getting a new instance. Another idea is to have the types register themself:
// in base.hpp:
template<typename T> Base * createT() { return new T; }
struct BaseFactory {
typedef std::map<std::string, Base*(*)()> map_type;
static Base * createInstance(std::string const& s) {
map_type::iterator it = getMap()->find(s);
if(it == getMap()->end())
return 0;
return it->second();
}
protected:
static map_type * getMap() {
// never delete'ed. (exist until program termination)
// because we can't guarantee correct destruction order
if(!map) { map = new map_type; }
return map;
}
private:
static map_type * map;
};
template<typename T>
struct DerivedRegister : BaseFactory {
DerivedRegister(std::string const& s) {
getMap()->insert(std::make_pair(s, &createT<T>));
}
};
// in derivedb.hpp
class DerivedB {
...;
private:
static DerivedRegister<DerivedB> reg;
};
// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");
You could decide to create a macro for the registration
#define REGISTER_DEC_TYPE(NAME) \
static DerivedRegister<NAME> reg
#define REGISTER_DEF_TYPE(NAME) \
DerivedRegister<NAME> NAME::reg(#NAME)
I'm sure there are better names for those two though. Another thing which probably makes sense to use here is shared_ptr
.
If you have a set of unrelated types that have no common base-class, you can give the function pointer a return type of boost::variant<A, B, C, D, ...>
instead. Like if you have a class Foo, Bar and Baz, it looks like this:
typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() {
return variant_type(T());
}
typedef std::map<std::string, variant_type (*)()> map_type;
A boost::variant
is like an union. It knows which type is stored in it by looking what object was used for initializing or assigning to it. Have a look at its documentation here. Finally, the use of a raw function pointer is also a bit oldish. Modern C++ code should be decoupled from specific functions / types. You may want to look into Boost.Function
to look for a better way. It would look like this then (the map):
typedef std::map<std::string, boost::function<variant_type()> > map_type;
std::function
will be available in the next version of C++ too, including std::shared_ptr
.