Domanda

Ho un file:Base.h

class Base;
class DerivedA : public Base;
class DerivedB : public Base;

/*etc...*/

e un altro file: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.*/

C'è un modo per convertire in qualche modo questa stringa tipo (classe), in modo che BaseFactory non è necessario conoscere tutte le possibili classi Derivate, e se() per ogni uno di loro?Posso produrre una classe da questa stringa?

Penso che questo può essere fatto in C#, attraverso la Riflessione.C'è qualcosa di simile in C++?

È stato utile?

Soluzione

No, non c'è nessuno, a meno che non la mappatura da soli. C ++ non ha alcun meccanismo per creare oggetti cui tipi sono determinati in fase di esecuzione. È possibile utilizzare una mappa per farlo mappatura da soli, anche se:

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>;

E allora si può fare

return map[some_string]();

Come una nuova istanza. Un'altra idea è quella di avere i tipi registrano themself:

// 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");

Si potrebbe decidere di creare una macro per la registrazione

#define REGISTER_DEC_TYPE(NAME) \
    static DerivedRegister<NAME> reg

#define REGISTER_DEF_TYPE(NAME) \
    DerivedRegister<NAME> NAME::reg(#NAME)

Sono sicuro che ci sono più nomi per quei due però. Un'altra cosa che probabilmente ha senso utilizzare qui è shared_ptr.

Se si dispone di un insieme di tipi indipendenti che non hanno classe base comune, si può dare la funzione di puntamento un tipo di ritorno di boost::variant<A, B, C, D, ...> invece. Come se si dispone di una classe Foo, Bar e Baz, sembra che questo:

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;

Un boost::variant è come un sindacato. Si sa quale tipo memorizzato in essa, cercando quale oggetto è stato utilizzato per l'inizializzazione o l'assegnazione ad essa. Dai un'occhiata alla sua documentazione qui . Infine, l'uso di un puntatore a funzione crudo è anche un po 'usurate. moderno codice C ++ dovrebbe essere disaccoppiato da specifiche funzioni / tipi. Si consiglia di guardare in Boost.Function per cercare un modo migliore. Sarebbe simile a questa, allora (mappa):

typedef std::map<std::string, boost::function<variant_type()> > map_type;

std::function sarà disponibile nella prossima versione di C ++ anche, compreso std::shared_ptr.

Altri suggerimenti

No, non c'è. La mia soluzione preferita a questo problema è quello di creare un dizionario che mappa nome al metodo di creazione. Le classi che vogliono essere creati in questo modo quindi registrare un metodo di creazione con il dizionario. Questo è discusso in dettaglio nella GoF modelli libro .

La risposta breve è che si non può. Vedere queste SO domande per il motivo:

  1. Perché C ++ non ha riflesso?
  2. Come posso aggiungere riflessione a un'applicazione C ++?

ho risposto in un altro SO domanda sulle fabbriche C ++. Consulta ci se una fabbrica flessibile è di interesse. Provo a descrivere un vecchio modo da ET ++ per utilizzare le macro che ha lavorato più grande per me.

ET ++ era un progetto per porto vecchio MacApp al C ++ e X11. Nello sforzo di esso Eric Gamma ecc iniziato a pensare a Design Patterns

boost::funzionale, ha una fabbrica modello, che è molto flessibile: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html

La mia preferenza però per generare classi wrapper che nascondono la mappatura e la creazione di oggetti meccanismo.Lo scenario comune che incontro è la necessità di associare diverse classi derivate di una classe di base per le chiavi, dove le classi derivate tutti hanno un comune costruttore firma.Ecco la soluzione che ho creato finora.

#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

Io sono generalmente contrario di pioggia macro usa, ma ho fatto un'eccezione qui.Il codice di cui sopra genera GENERIC_FACTORY_MAX_ARITY + 1 versioni di una classe denominata GenericFactory_N, per ogni N tra 0 e GENERIC_FACTORY_MAX_ARITY inclusive.

Utilizzando la classe generata modelli è facile.Si supponga che si desideri una fabbrica di creare BaseClass oggetti derivati utilizzando una stringa di mappatura.Ognuno dei oggetti derivati da prendere 3 numeri interi come parametri del costruttore.

#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);

Il GenericFactory_N distruttore della classe virtuale per la.

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;

Nota che questa linea generico di fabbrica generatore di macro

#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"

Assume il generico fabbrica di intestazione del file è denominato GenericFactory.hpp

soluzione dettagliata per la registrazione degli oggetti, e li si accede con nomi di stringa.

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_ */

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_ */

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;
}

compilare ed eseguire esso (ho fatto questo con Eclipse)

Output:

Base constructor
test1 constructor m_a=10m_b=20
Base constructor
test1 constructor m_a=30m_b=40

Significato riflessione come in Java. c'è qualche info qui: http://msdn.microsoft.com/en-us /library/y0114hz2(VS.80).aspx

In linea generale, la ricerca su Google per "c ++ riflessione"

Tor Brede Vekterli fornisce un'estensione spinta che dà esattamente le funzionalità che cercate. Attualmente, è giusto un po 'imbarazzante con librerie Boost attuali, ma sono stato in grado di farlo funzionare con 1.48_0 dopo aver cambiato il suo spazio dei nomi di base.

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

In risposta a coloro che mettono in dubbio il motivo per cui una cosa del genere (come riflessione) sarebbe utile per C ++ - Io lo uso per le interazioni tra l'interfaccia utente e un motore - l'utente seleziona un'opzione nell'interfaccia utente, e il motore prende l'interfaccia utente stringa di selezione, e produce un oggetto del tipo desiderato.

Il vantaggio principale di utilizzare il framework qui (oltre il mantenimento di un frutto-list da qualche parte) è che la funzione di registrazione è nella definizione di ogni classe (e richiede solo una riga di codice chiamando la funzione di registrazione per classe registrata) - al contrario di un file contenente il frutto-list, che deve essere aggiunto manualmente ogni volta che una nuova classe è derivata.

Ho fatto la fabbrica un membro statico della mia classe di base.

Questo è il modello di fabbrica. Vedere wikipedia (e questo esempio). Non è possibile creare un tipo di per sé da una stringa senza qualche trucco eclatanti. Perché avete bisogno di questo?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top