Existe uma maneira de instanciar objects de uma string contendo o nome da class?

Eu tenho um arquivo: Base.h

class Base; class DerivedA : public Base; class DerivedB : public Base; /*etc...*/ 

e outro arquivo: 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.*/ 

Existe uma maneira de converter essa string de alguma forma em um tipo real (class), para que BaseFactory não precise saber todas as classs Derived possíveis e ter if () para cada uma delas? Posso produzir uma class dessa string?

Eu acho que isso pode ser feito em C # através do Reflection. Existe algo semelhante em C ++?

    Não, não há nenhum, a menos que você faça o mapeamento sozinho. C ++ não possui mecanismo para criar objects cujos tipos são determinados em tempo de execução. Você pode usar um mapa para mapear você mesmo:

     template Base * createInstance() { return new T; } typedef std::map map_type; map_type map; map["DerivedA"] = &createInstance; map["DerivedB"] = &createInstance; 

    E então você pode fazer

     return map[some_string](); 

    Obtendo uma nova instância. Outra ideia é que os tipos se registrem:

     // in base.hpp: template Base * createT() { return new T; } struct BaseFactory { typedef std::map 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 struct DerivedRegister : BaseFactory { DerivedRegister(std::string const& s) { getMap()->insert(std::make_pair(s, &createT)); } }; // in derivedb.hpp class DerivedB { ...; private: static DerivedRegister reg; }; // in derivedb.cpp: DerivedRegister DerivedB::reg("DerivedB"); 

    Você pode decidir criar uma macro para o registro

     #define REGISTER_DEC_TYPE(NAME) \ static DerivedRegister reg #define REGISTER_DEF_TYPE(NAME) \ DerivedRegister NAME::reg(#NAME) 

    Tenho certeza de que há nomes melhores para esses dois. Outra coisa que provavelmente faz sentido usar aqui é shared_ptr .

    Se você tem um conjunto de tipos não relacionados que não possuem class base comum, você pode dar ao ponteiro de function um tipo de retorno de boost::variant . Como se você tivesse uma class como Foo, Bar e Baz, seria assim:

     typedef boost::variant variant_type; template variant_type createInstance() { return variant_type(T()); } typedef std::map map_type; 

    Um boost::variant é como uma união. Ele sabe qual tipo é armazenado nele, procurando qual object foi usado para inicializar ou atribuir a ele. Dê uma olhada em sua documentação aqui . Finalmente, o uso de um ponteiro de function bruta também é um pouco antigo. O código C ++ moderno deve ser dissociado de funções / tipos específicos. Você pode querer olhar para Boost.Function para procurar uma maneira melhor. Ficaria assim então (o mapa):

     typedef std::map > map_type; 

    std::function estará disponível na próxima versão do C ++ também, incluindo std::shared_ptr .

    Não não há. Minha solução preferida para esse problema é criar um dictionary que mapeie o nome para o método de criação. Classes que querem ser criadas assim registram um método de criação com o dictionary. Isso é discutido com algum detalhe no livro de padrões do GoF .

    A resposta curta é que você não pode. Veja estas perguntas sobre o motivo:

    1. Por que o C ++ não tem reflection?
    2. Como posso adicionar reflection a um aplicativo C ++?

    Eu respondi em outra pergunta sobre as fábricas de C ++. Por favor, veja lá se uma fábrica flexível é de interesse. Eu tento descrever uma maneira antiga do ET ++ para usar macros que funcionou muito bem para mim.

    ET ++ foi um projeto para portar MacApp antigo para C ++ e X11. No esforço dele Eric Gamma etc começou a pensar em Design Patterns

    O boost :: functional tem um modelo de fábrica bastante flexível: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html

    Minha preferência, no entanto, é gerar classs de wrappers que ocultam o mecanismo de mapeamento e criação de objects. O cenário comum que encontro é a necessidade de mapear diferentes classs derivadas de alguma class base para keys, onde todas as classs derivadas têm uma assinatura de construtor comum disponível. Aqui está a solução que cheguei até agora.

     #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  #include  #include  #include  //The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classs 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 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  void Register(const KeyType& key) { m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl, 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  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 CreatorFuncType; typedef std::unordered_map CreatorMapType; CreatorMapType m_creatorMap; }; #undef N #undef GENERIC_FACTORY_APPEND_PLACEHOLDER #endif // defined(BOOST_PP_IS_ITERATING) #endif // include guard 

    Eu geralmente me oponho ao uso pesado de macro, mas eu fiz uma exceção aqui. O código acima gera GENERIC_FACTORY_MAX_ARITY + 1 versões de uma class chamada GenericFactory_N, para cada N entre 0 e GENERIC_FACTORY_MAX_ARITY, inclusive.

    Usando os modelos de class gerados é fácil. Suponha que você queira que uma fábrica crie objects derivados de BaseClass usando um mapeamento de string. Cada um dos objects derivados toma 3 inteiros como parâmetros construtores.

     #include "GenericFactory.hpp" typedef GenericFactory_3, int, int int> factory_type; factory_type factory; factory.Register("DerivedType1"); factory.Register("DerivedType2"); factory.Register("DerivedType3"); factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3); factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6); 

    O destruidor da class GenericFactory_N é virtual para permitir o seguinte.

     class SomeBaseFactory : public GenericFactory_2 { public: SomeBaseFactory() : GenericFactory_2() { Register(1); Register(2); } }; SomeBaseFactory factory; SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true); delete someObject; 

    Observe que esta linha da macro do gerador genérico de fábrica

     #define BOOST_PP_FILENAME_1 "GenericFactory.hpp" 

    Supõe que o arquivo de header genérico de fábrica é denominado GenericFactory.hpp

    Reflexão de significado como em Java. Há algumas informações aqui: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx

    De um modo geral, procure no Google por “reflection c + +”

     --------------- Detail solution for registering the objects, and accessing them with string names. --------------- 1. common.h #ifndef COMMON_H_ #define COMMON_H_ #include #include #include #include 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="< Base * createInstance(int a, int b) { return new T(a,b); } typedef std::map map_type; map_type mymap; int main() { mymap["test1"] = &createInstance; mymap["test2"] = &createInstance; /*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 

    Este é o padrão de fábrica. Veja wikipedia (e este exemplo). Você não pode criar um tipo per se de uma string sem algum hack notório. Por que você precisa disso?

    Tor Brede Vekterli fornece uma extensão de impulso que dá exatamente a funcionalidade que você procura. Atualmente, é um pouco desajeitado com as bibliotecas atuais, mas eu consegui trabalhar com 1.48_0 depois de mudar seu namespace base.

    http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference

    Em resposta àqueles que questionam por que tal coisa (como reflection) seria útil para c ++ – eu a utilizo para interações entre a interface do usuário e um mecanismo – o usuário seleciona uma opção na interface do usuário e o mecanismo usa a string de seleção da interface do usuário. e produz um object do tipo desejado.

    O principal benefício de usar a estrutura aqui (manter uma lista de frutas em algum lugar) é que a function de registro está na definição de cada class (e requer apenas uma linha de código chamando a function de registro por class registrada) – em oposição a um arquivo contendo a lista de frutas, que deve ser adicionada manualmente a cada vez que uma nova class é derivada.

    Eu fiz da fábrica um membro estático da minha class base.