Use String-To-Class Lookup table in C++ to instantiate classes
寻找避免大量if/else的方法,并使用查找表将字符串解析为要实例化的特定类,这些类都是从基类派生的。这样的事情可能发生吗?如果可能,怎么可能?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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"); } |
如果编译器与C++ 11兼容,那么您可以很容易地用lambdas和eDCOX1 1来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #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; } |
其思想是创建一个映射,该映射为您提供一个生成适当子类的函数。
IDENO演示。
首先,语法入门:
1 2 3 | struct Base { virtual ~Base() {} // do not forget this if you need polymorphism }; |
然后,"工厂"功能:
1 2 | template <typename T> std::unique_ptr<Base> makeBase() { return std::unique_ptr<Base>(new T{}); } |
此函数的类型为:
1 | using BaseMaker = std::unique_ptr<Base>(*)(); |
最后,总而言之:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(); } |
您应该能够执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 (); |
不过,我更喜欢
不能这样初始化列表。
1 2 3 4 5 6 7 8 9 10 | typedef struct { const char *name; BaseClass class; } LookupList; LookupList list[] = { {"ClassOne", DerivedClassOne}, {"ClassTwo", DerivedClassTwo} }; |
list.class是一个baseclass对象,而初始化值是派生的classone,它是一个类。这没有道理。您将得到编译器错误。
基本上,你可能正在寻找C++中的反射。有个讨论,你可能会发现一些有用的东西如何向C++应用程序添加反射?