C++ generic object factory by string name
我需要一种方法来根据作为std::string传递的类名实例化对象。这目前有效,但需要概括:
1 2 3 4 5 | void* create(std::string name) { if(name =="classOne") return new ClassOne(); else if(name =="classTwo") return new ClassTwo(); /* ... */ } |
我没有的:
- 对要实例化的类的控制:可以是30个方类。不能对此类进行任何更改(例如,基祖先、多态创建者方法等…)
- 完整的类名列表:以后可以添加更多的类,并且不应该在这个工厂中发生更改。
- 围绕要实例化的类的包装器:由于前面两个点。
别的什么都可以。
最佳用例场景将是:
1 2 3 4 5 6 7 | int main() { void *obj = create("classTree"); // create object based on the string name /* ... */ // once we know by context which specific class we are dealing with ClassTree *ct = (ClassTree*)obj; // cast to appropiate class std::cout << ct->getSomeText() << std::endl; // use object } |
考虑到要实例化的对象可能来自一个类或一个结构,作为一个侧面,也可能是不相关的注释。
添加的信息
我看到需要更多的上下文。下面是我的特定用例,简化了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // registration mechanism int main() { std::map< std::string, void(*func)(std::string, void*) > processors; // map of processors by class name processors["ClassFour"] = (void(*)(std::string, void*)) &classFourMessageProcessor; // register processor (cast needed from specific to generic) } // function receiving string messages void externalMessageHandler(std::string msg) { std::string objType = extractTypeFromMessageHeader(msg); // extract type from message // now that we know what we are dealing with, create the specific object void *obj = create(objType); // << creator needed processors[objType](msg, obj); // dispatch message to process } // previously registered message processor void classFourMessageProcessor(std::String msg, ClassFour *obj) { std::string streetAddress = msg.substr(10, 15); // knowing the kind of message we can extract information obj->moveTheEtherTo(streetAddress); // use the created object } |
添加的信息
我用C++ 11用最新的GNU编译器。
您可以为每种类类型存储一个工厂函数。一个简单的方法是使用模板
1 2 3 4 | template <typename T> void* creator() { return new T(); } |
并将它们存储在地图中(即"class4"链接到
编辑:为了澄清,
1 2 3 4 5 | typedef void* (*CreatorFunc)(); typedef void (*ProcessorFunc)(std::string, void*); typedef std::pair<CreatorFunc, ProcessorFunc> Entry; std::map< std::string, Entry > processors; |
添加一个新类非常简单
1 | processors["SomeClass"] = Entry(creator<SomeClass>, ClassFourMessageProcessor); |
也许下面的关于lokup表的讨论将是一个很好的解决方案。
(注:我不知道你使用的是编译器,所以这个解决方案是C++ 03,如果你使用的是带有C++ 11支持的编译器,你可以使用无序的地图代替地图)
(注意2:您也可以使用智能指针,并注意返回值,在这个示例中,我只想显示一个函数)
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include <iostream> #include <string> #include <map> #include <vector> struct ClassMaker { virtual void* getInstance() const = 0; virtual ~ClassMaker() {} }; class Factory { private: std::map<std::string, ClassMaker*> lokupTable; typedef typename std::map<std::string, ClassMaker*>::iterator Iterator; public: void addClass(const std::string& key, ClassMaker* const newClassMaker) { lokupTable[key] = newClassMaker; } void* create(const std::string& key) { void* result = NULL; Iterator it = lokupTable.find(key); if(it != lokupTable.end()) result = (it->second)->getInstance(); return result; } void releaseTable() { for (Iterator it = lokupTable.begin(); it != lokupTable.end(); ++it) delete it->second; } }; struct IntCreator : public ClassMaker { void* getInstance() const { return new int; } }; struct StringCreator : public ClassMaker { void* getInstance() const { return new std::string; } }; int main() { Factory myFactory; myFactory.addClass("int", new IntCreator); myFactory.addClass("string", new StringCreator); int* myInt = reinterpret_cast<int*>(myFactory.create("int")); *myInt = 10; std::cout<< *myInt << std::endl; delete myInt; myFactory.releaseTable(); return 0; } |
以下是一个例子:
现在,您没有对类进行任何更改,并且可以在不重新编程工厂的情况下添加更多的类。您可能会将至少1作为模板-一定要让编译器实例化特定的实现。
你会考虑Boost.mpl吗?与STL不同,它允许创建包含类型而不是实例的容器。有一个从字符串到类型的映射会给你想要的工厂,不是吗?