有没有办法从一个string实例化对象的类名称?
我有一个文件:Base.h
class Base; class DerivedA : public Base; class DerivedB : public Base; /*etc...*/
和另一个文件: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.*/
有没有办法以某种方式将此string转换为实际types(类),以便BaseFactory不必知道所有可能的派生类,并有每个他们的if()? 我可以从这个string中产生一个类吗?
我认为这可以通过反思在C#中完成。 在C ++中有类似的东西吗?
不,没有,除非你自己做映射。 C ++没有机制来创buildtypes在运行时确定的对象。 尽pipe如此,您可以使用地图来完成自己的映射:
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>;
然后你可以做
return map[some_string]();
获得新的实例。 另一个想法是让types注册自己:
// 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");
您可以决定为注册创build一个macros
#define REGISTER_DEC_TYPE(NAME) \ static DerivedRegister<NAME> reg #define REGISTER_DEF_TYPE(NAME) \ DerivedRegister<NAME> NAME::reg(#NAME)
我相信这两个人有更好的名字。 另一个可能在这里使用的东西是shared_ptr
。
如果你有一组不相关的types没有共同的基类,你可以给函数指针一个返回types的boost::variant<A, B, C, D, ...>
来代替。 就像如果你有一个Foo,Bar和Baz的课程,看起来像这样:
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;
boost::variant
类似于union。 它通过查看用于初始化或分配的对象知道哪个types存储在其中。 看看这里的文档。 最后,使用一个原始的函数指针也有点古怪。 现代C ++代码应该从特定的function/types中分离出来。 您可能需要考虑Boost.Function
来寻找更好的方法。 它会看起来像这样(地图):
typedef std::map<std::string, boost::function<variant_type()> > map_type;
std::function
也可以在下一个版本的C ++中使用,包括std::shared_ptr
。
没有没有。 我对这个问题的首选解决scheme是创build一个名称映射到创build方法的字典。 想要像这样创build的类然后在字典中注册一个创build方法。 这在GoF模式书中有详细的讨论。
简短的答案是你不能。 看到这些SO问题为什么:
- 为什么C ++没有reflection?
- 我怎样才能将reflection添加到C ++应用程序?
我已经在另一个有关C ++工厂的问题中回答了。 如果一个灵活的工厂感兴趣,请看那里 。 我尝试从ET ++中描述一种老式的方式来使用对我来说很好的macros。
ET ++是一个将旧的MacApp移植到C ++和X11的项目。 Eric Gamma等人开始考虑devise模式
boost :: functional有一个相当灵活的工厂模板: http : //www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
我的偏好是生成隐藏映射和对象创build机制的包装类。 我遇到的常见情况是需要将某些基类的不同派生类映射到键,其中派生类都具有可用的公用构造函数签名。 这是我迄今为止所提出的解决scheme。
#ifndef GENERIC_FACTORY_HPP_INCLUDED //BOOST_PP_IS_ITERATING is defined when we are iterating over this header file. #ifndef BOOST_PP_IS_ITERATING //Included headers. #include <unordered_map> #include <functional> #include <boost/preprocessor/iteration/iterate.hpp> #include <boost/preprocessor/repetition.hpp> //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated. #ifndef GENERIC_FACTORY_MAX_ARITY #define GENERIC_FACTORY_MAX_ARITY 10 #endif //This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class. //Each class generated will have a suffix of the number of parameters taken by the derived type constructors. #define BOOST_PP_FILENAME_1 "GenericFactory.hpp" #define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY) #include BOOST_PP_ITERATE() #define GENERIC_FACTORY_HPP_INCLUDED #else #define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file. #define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1)) //This is the class which we are generating multiple times template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)> class BOOST_PP_CAT(GenericFactory_, N) { public: typedef BasePointerType result_type; public: virtual ~BOOST_PP_CAT(GenericFactory_, N)() {} //Registers a derived type against a particular key. template <class DerivedType> void Register(const KeyType& key) { m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N)); } //Deregisters an existing registration. bool Deregister(const KeyType& key) { return (m_creatorMap.erase(key) == 1); } //Returns true if the key is registered in this factory, false otherwise. bool IsCreatable(const KeyType& key) const { return (m_creatorMap.count(key) != 0); } //Creates the derived type associated with key. Throws std::out_of_range if key not found. BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const { return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a)); } private: //This method performs the creation of the derived type object on the heap. template <class DerivedType> BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a)) { BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a))); return pNewObject; } private: typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType; typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType; CreatorMapType m_creatorMap; }; #undef N #undef GENERIC_FACTORY_APPEND_PLACEHOLDER #endif // defined(BOOST_PP_IS_ITERATING) #endif // include guard
我一般反对重度使用macros,但是我在这里做了一个例外。 上面的代码为0和GENERIC_FACTORY_MAX_ARITY(含)之间的每个N生成一个名为GenericFactory_N的类的GENERIC_FACTORY_MAX_ARITY + 1版本。
使用生成的类模板很容易。 假设您希望工厂使用string映射来创buildBaseClass派生对象。 每个派生对象都有3个整数作为构造函数参数。
#include "GenericFactory.hpp" typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type; factory_type factory; factory.Register<DerivedClass1>("DerivedType1"); factory.Register<DerivedClass2>("DerivedType2"); factory.Register<DerivedClass3>("DerivedType3"); factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3); factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);
GenericFactory_N类的析构函数是虚拟的,以允许以下内容。
class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool> { public: SomeBaseFactory() : GenericFactory_2() { Register<SomeDerived1>(1); Register<SomeDerived2>(2); } }; SomeBaseFactory factory; SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true); delete someObject;
请注意,通用工厂生成器macros的这一行
#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
假定通用工厂头文件被命名为GenericFactory.hpp
如Java中的意思reflection。 这里有一些信息: http : //msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
一般来说,search谷歌的“C ++reflection”
--------------- Detail solution for registering the objects, and accessing them with string names. --------------- 1. common.h #ifndef COMMON_H_ #define COMMON_H_ #include<iostream> #include<string> #include<iomanip> #include<map> using namespace std; class Base{ public: Base(){cout <<"Base constructor\n";} virtual ~Base(){cout <<"Base destructor\n";} }; #endif /* COMMON_H_ */ 2. test1.h /* * test1.h * * Created on: 28-Dec-2015 * Author: ravi.prasad */ #ifndef TEST1_H_ #define TEST1_H_ #include "common.h" class test1: public Base{ int m_a; int m_b; public: test1(int a=0, int b=0):m_a(a),m_b(b) { cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl; } virtual ~test1(){cout <<"test1 destructor\n";} }; #endif /* TEST1_H_ */ 3. test2.h #ifndef TEST2_H_ #define TEST2_H_ #include "common.h" class test2: public Base{ int m_a; int m_b; public: test2(int a=0, int b=0):m_a(a),m_b(b) { cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl; } virtual ~test2(){cout <<"test2 destructor\n";} }; #endif /* TEST2_H_ */ 3. main.cpp #include "test1.h" #include "test2.h" template<typename T> Base * createInstance(int a, int b) { return new T(a,b); } typedef std::map<std::string, Base* (*)(int,int)> map_type; map_type mymap; int main() { mymap["test1"] = &createInstance<test1>; mymap["test2"] = &createInstance<test2>; /*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it) std::cout << it->first << " => " << it->second(10,20) << '\n';*/ Base *b = mymap["test1"](10,20); Base *b2 = mymap["test2"](30,40); return 0; } ------------------------ Compile and Run it (Have done this with Eclipse) ------------------------ /Output Base constructor test1 constructor m_a=10m_b=20 Base constructor test1 constructor m_a=30m_b=40
这是工厂模式。 看维基百科(和这个例子)。 你不能从一个string本身创build一个types,没有一些恶意的黑客。 你为什么需要这个?
Tor Brede Vekterli提供了一个扩展function,可以提供您所需的function。 目前,与当前的boost库很相似,但是在更改基本名称空间之后,我可以用1.48_0来处理它。
在回答那些质疑为什么这样的事情(如反思)对于c ++有用的人的问题中,我将其用于UI和引擎之间的交互 - 用户在UI中select一个选项,并且引擎采用UIselectstring,并产生所需types的对象。
这里使用框架的主要好处是(在维护一个水果列表的地方)是注册函数在每个类的定义中(并且只需要一行代码调用每个注册类的注册函数) – 而不是一个包含水果列表,必须手动添加到每一个新的类派生。
我使工厂成为我的基类的静态成员。