Is there a way to instantiate objects from a string holding their class name?

Viewed 93691

I have a file: Base.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

and another file: BaseFactory.h

#include "Base.h"

class BaseFactory
{
public:
  BaseFactory(const string &sClassName){msClassName = sClassName;};

  Base * Create()
  {
    if(msClassName == "DerivedA")
    {
      return new DerivedA();
    }
    else if(msClassName == "DerivedB")
    {
      return new DerivedB();
    }
    else if(/*etc...*/)
    {
      /*etc...*/
    }
  };
private:
  string msClassName;
};

/*etc.*/

Is there a way to somehow convert this string to an actual type (class), so that BaseFactory wouldn't have to know all the possible Derived classes, and have if() for each one of them? Can I produce a class from this string?

I think this can be done in C# through Reflection. Is there something similar in C++?

12 Answers

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.

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.

I have answered in another SO question about C++ factories. Please see there if a flexible factory is of interest. I try to describe an old way from ET++ to use macros which has worked great for me.

ET++ was a project to port old MacApp to C++ and X11. In the effort of it Eric Gamma etc started to think about Design Patterns

This is the factory pattern. See wikipedia (and this example). You cannot create a type per se from a string without some egregious hack. Why do you need this?

Yes, it is possible, without the use of frameworks and macros, just getting the memory address of the class methods and constructors. You can retrieve them from the map generated by the linker, when configured for this action.

visit this site

https://ealaframework.no-ip.org/wiki/page/c.reference

A C++11-style full example:

// Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;

// BaseFactory.h
class BaseFactory
{
public:
    static BaseFactory& get() {
        static BaseFactory singleton;
        return singleton;
    }

    virtual ~BaseFactory() {};

    BaseFactory(const BaseFactory&) = delete;
    BaseFactory(BaseFactory&&) = delete;

    template <class DerivedClass>
    static std::shared_ptr<Base> creator()
    {
        return std::shared_ptr<Base>(new DerivedClass());
    }

    template <class DerivedClass>
    void register_class(const std::string& class_name)
    {
        if (name_to_creator_map.find(class_name) == name_to_creator_map.end())
        {
            std::function<std::shared_ptr<Base>(void)> functor = &BaseFactory::template creator<DerivedClass>;
            name_to_creator_map.emplace(class_name, functor);
        }
    }

    std::shared_ptr<Base> create(const std::string& class_name) const;

private:
    BaseFactory();

    std::map<std::string, std::function<std::shared_ptr<Base>(void)>> name_to_creator_map;
};

// example.cpp using BaseFactory
BaseFactory::get().register_class<DerivedA>("DerivedA");
BaseFactory::get().register_class<DerivedB>("DerivedB");
auto a_obj = BaseFactory::get().create("DerivedA");
auto b_obj = BaseFactory::get().create("DerivedB");

There is a ready-to-use reflection library https://www.rttr.org/ . You can easily instantiate class by string with it.

struct MyStruct { MyStruct() {}; void func(double) {}; int data; };

RTTR_REGISTRATION
{
    registration::class_<MyStruct>("MyStruct")
         .constructor<>()
         .property("data", &MyStruct::data)
         .method("func", &MyStruct::func);
}


type t = type::get_by_name("MyStruct");
variant var = t.create();
Related