Use String-To-Class lookup table in C ++ to instantiate classes

Looking for a way to avoid massive IF / ELSE and use a lookup table to resolve strings for specific classes to create instances that all come from the base class. Something like this is possible, and if so, how?

typedef struct BaseClass { } BaseClass; typedef struct DerivedClassOne : BaseClass { } DerivedClassOne; typedef struct DerivedClassTwo : BaseClass { } DerivedClassTwo; typedef struct { const char *name; BaseClass class; } LookupList; LookupList list[] = { {"ClassOne", DerivedClassOne}, {"ClassTwo", DerivedClassTwo} }; BaseClass *InstantiateFromString(char *name) { int i; for (i = 0; i < 2; i++) { if (!strcmp(name, list[i].name)) return new list[i].class(); } } int main (int argc, char *argv[]) { BaseClass *myObjectFromLookup = InstantiateFromString("ClassOne"); } 
+4
source share
5 answers

If your compiler is compatible with C ++ 11, you can easily do this with lambdas and std::map :

 #include <iostream> #include <string> #include <map> #include <functional> using namespace std; struct BaseClass {virtual void foo()=0;}; struct DerivedClass1 : public BaseClass {void foo() {cout << "1" << endl;}}; struct DerivedClass2 : public BaseClass {void foo() {cout << "2" << endl;}}; // Here is the core of the solution: this map of lambdas does all the "magic" map<string,function<BaseClass*()> > factory { {"one", [](){return new DerivedClass1();}} , {"two", [](){return new DerivedClass2();}} }; int main() { BaseClass *a = factory["one"](); // Note the function call () at the end BaseClass *b = factory["two"](); a->foo(); b->foo(); delete a; delete b; return 0; } 

The idea is to create a map that gives you a function that makes an appropriate subclass.

Demo on ideon .

+4
source

First of all, the syntax primer:

 struct Base { virtual ~Base() {} // do not forget this if you need polymorphism }; 

Then the "factory" function:

 template <typename T> std::unique_ptr<Base> makeBase() { return std::unique_ptr<Base>(new T{}); } 

Type of this function:

 using BaseMaker = std::unique_ptr<Base>(*)(); 

And finally putting it at all:

 struct DerivedOne: Base {}; struct DerivedTwo: Base {}; using BaseMakerMap = std::map<std::string, BaseMaker>; BaseMakerMap const map = { { "DerivedOne", makeBase<DerivedOne> }, { "DerivedTwo", makeBase<DerivedTwo> } }; std::unique_ptr<Base> makeFromName(std::string const& n) { BaseMakerMap::const_iterator it = map.find(n); if (it == map.end()) { return std::unique_ptr<Base>(); } // not found BaseMaker maker = it->second; return maker(); } 
+3
source

Basically, you can look for reflection in C ++, I think. there is a discussion that you can find something useful there How to add reflection to a C ++ application?

0
source

You cannot initialize your list as follows.

 typedef struct { const char *name; BaseClass class; } LookupList; LookupList list[] = { {"ClassOne", DerivedClassOne}, {"ClassTwo", DerivedClassTwo} }; 

List.class is a BaseClass object, and the initialization value is DerivedClassOne, which is a class. It does not make sense. You will get a compiler error.

0
source

You should be able to do the following:

 template<class C> BaseClass * makeObject<C> () { return new C; } struct LookupList { const char* name; BaseClass * (*factoryFunction) (); }; LookupList list [] = { {"ClassOne", &makeObject<DerivedClassOne>}, {"ClassTwo", &makeObject<DerivedClassTwo>} }; ... ... instantiateFromString ... return list[i].factoryFunction (); 

I would prefer to use a map above the array for LookupList . In addition, you can familiarize yourself with the functional syntax in C ++ 11.

0
source

Source: https://habr.com/ru/post/1492463/


All Articles