Reputation: 11506
I need to port the following Java concept into C++ :
Hash map holding object id key and class type value:
Map<String, Class> _objectsBank = new HashMap<>();
Somewhere in the init method I fill the bank like this:
_objectsBank .put("CLASS_ID_1", MyClass1.class);
_objectsBank .put("CLASS_ID_2", MyClass2.class);
....
Then , later ,I construct an instance of one of the classes saved in that bank by demand. Kind of "lazy" init :
private MyClass initNewProg(String name) {
MyClass instance;
try {
Class cl = _objectsBank.get(name);
java.lang.reflect.Constructor co = cl.getConstructor(String.class);
instance= (MyClass) co.newInstance(name);
return instance;
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{
e.printStackTrace();
return null;
}
}
How would I do it in C++ ? How can I set a class type as std::map value so that I can query it later to construct an appropriate instance from it? Is there something like this in Boost libraries?
Upvotes: 0
Views: 2697
Reputation: 4110
Take a look at Is there a way to instantiate objects from a string holding their class name? and Instantiating classes by name with factory pattern, which describe the problem and multiple approaches with pros & cons of each ones. The examples cover registration of contructors for classes, which solves the issue with multiple if-statements you mentioned in @Karthik T's solution.
Upvotes: 3
Reputation: 9555
You could use function pointers. As you cannot take a function pointer to a constructor you must use a factory function:
template <class Derived>
Base* create()
{
return new Derived;
}
Then you can save function pointers to template instances of the factory function in the map to construct your derived classes:
int main()
{
std::map<std::string, Base*(*)()> classMap;
classMap["Derived1"] = &create<Derived1>;
classMap["Derived2"] = &create<Derived2>;
delete classMap["Derived1"]();
delete classMap["Derived2"]();
}
See it on Ideone
Upvotes: 5
Reputation: 62906
If using Qt and making your objects into QObjects is an option, then it can do this via Qt metaobject system. Practical starting points for looking into it are QObject::metaObject()
and QMetaObject::newInstance()
method documentations.
This also illustrates the point, that getting feature like this in C++ more-or-less requires extra preprocessing step, such as Qt's moc
, which creates extra .cpp file with extra methods and data to support this.
A more lightweight option might be developing your own simple pre-processor, which for example reads some special comment notation in your .cpp files, and perhaps generates code to create a map, which maps object type to function which instantiates the object, or something. I'm not sure you could coax C++ template system to do most of the work for you.
Upvotes: 0
Reputation: 31972
One option is that instead of the class name, you can store a string representing the instance, and later use a factory method to return the correct instance. But this will be clumsier since you would need to manually mimic the reflection process by doing the below.
MyClass* initNewProg(string name) {
if(name == "derived1")
return new Derived1();
else if(name == "derived2")
....
}
Another alternative is to use individual factory methods and function pointers.
typedef MyClass* (*MyFactoryFunc)();
map<string,MyFactoryFunc> myMap;
MyClass* createDerived1(){return new Derived1();}
...
MyClass* initNewProg(string name) {
return myMap[name]();
}
Upvotes: 1