[gelöst] Objekte anhand eines Typ String erstellen



  • Hi,

    Ich habe folgende Situation: Eine unbestimmte Anzahl unterschiedlicher aber von derselben Basisklasse abgeleitete Objekte soll erstellt und in einem Array vom Typ "Pointer zur Basisklasse" verwaltet werden.
    Dies funktioniert auch wunderbar in meinem Code. Aber ich möchte jetzt dass diese Liste nach dem Inhalt einer eingelesener Datei erstellt wird. In dieser Datei sollen die Objekte aufgelistet sein die ich bei Laufzeit erstellen und in der Liste speichern will.
    Ich kann natürlich eine Methode schreiben, die mit einem switch konstrukt für einen bestimmten Bezeichner ein entsprechendes Objekt erzeugt und dieses zurück liefert. Aber ich habe wirklich sehr viele unterschiedliche Objekte und emfinde dementsprechend dieses riesige switch konstukt als keine elegante Lösung.
    Deshalb jetzt meine Frage: gibt es irgendeine patentlösung um ein Objekt eines per String übergebenen Typs zu erzeugen?

    thx4answers
    Johann



  • Also wenn ich dich richtig verstehe, dann willst du Objekte erzeugen, deren Typ durch ein File bestimmt wird.
    So eine Art switch wirst du immer haben. Du musst ja irgendwo unterscheiden können was du erzeugen willst.

    Es läuft also auf eine Funktion der Art:

    object* create_object(string const& type);
    

    hinaus.

    Ob du da drin jetzt ein switch oder etwas ähnliches nimmst spielt ja keine Rolle mehr. Z.b kannst du eine map aufbauen, wo du halt zuerst die Typen registrieren musst, dann aber die Auswahl einfach implementieren kannst. Ein solches mapping von Text zu einem Typen hast du ja immer.

    Sprich: C++ hat keine eingebaute reflections.



  • Du hast mich auf jeden Fall richtig verstanden. Genau so eine create_object funktion will ich schreiben. Ich hatte halt nur gehofft es könnte für so etwas schon ein automatisiertes allgemeines Konzept geben. Aber ich muss mich wohl darauf einstellen so eine Zuweisung für ca. 150 Objecte selbst zu schreiben. 😞



  • Ich hab dazu was rumliegen:

    #include <boost/ptr_container/ptr_map.hpp>
    
    #include <iostream>
    #include <memory>
    #include <string>
    
    template<typename base_t>
    struct simple_instantiator {
      typedef base_t *pointer;
    
      virtual ~simple_instantiator() { }
      virtual pointer operator()() const = 0;
    };
    
    template<typename base_t, template<typename> class smart_ptr = ::std::auto_ptr>
    struct smart_ptr_instantiator {
      typedef smart_ptr<base_t> pointer;
    
      virtual ~smart_ptr_instantiator() { }
      virtual pointer operator()() const = 0;
    };
    
    template<typename instantiator_base_t>
    struct new_instantiators {
      template<typename derived_t>
      struct instantiator : public instantiator_base_t {
        typedef typename instantiator_base_t::pointer pointer;
    
        virtual pointer operator()() const { return pointer(new derived_t()); }
      };
    };
    
    template<typename key_t,
             typename base_t,
             typename instantiator_base_t = simple_instantiator<base_t>,
             template<typename derived_t> class instantiator_template = new_instantiators<instantiator_base_t>::template instantiator>
    class factory {
    public:
      typedef key_t               key_type;
      typedef base_t              base_type;
      typedef instantiator_base_t instantiator_type;
    
      typedef typename instantiator_type::pointer pointer;
    
      pointer create(key_type const &key) const {
        typename instantiator_map_type::const_iterator i = instantiators_.find(key);
    
        return i == instantiators_.end() ? pointer(0) : (*i->second)();
      }
    
      template<typename T>
      void register_type(key_type key) {
        instantiators_.insert(key, new instantiator_template<T>());
      }
    
    private:
      typedef boost::ptr_map<key_type, instantiator_type> instantiator_map_type;
    
      instantiator_map_type instantiators_;
    };
    
    ///////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////
    
    struct A {
      virtual ~A() { }
      virtual void foo() const { std::cout << 'A' << std::endl; }
    };
    
    struct B : A{
      virtual void foo() const { std::cout << 'B' << std::endl; }
    };
    
    struct C : A {
      virtual void foo() const { std::cout << 'C' << std::endl; }
    };
    
    int main() {
      factory<std::string, A, smart_ptr_instantiator<A> > fct;
    
      fct.register_type<B>("B");
      fct.register_type<C>("C");
    
      fct.create("B")->foo();
      fct.create("C")->foo();
    }
    

    Um das Registrieren der Typen wirst du nicht herumkommen, aber bei 150 Klassen sollte es sich schon lohnen, sie in einem Baum zu verstauen (bevor du jetzt ein laaanges if-else-if-else-if-else-if-Konstrukt baust).



  • Sowas in der Art ging mir auch durch den Kopf.



  • Kuhl...das ist genau was ich gesucht habe!
    Vielen Dank



  • hmm, ich habe jetzt eine ganze weile mit dem code rumgespielt, aber ich bekomme es doch noch nicht komplett für mein Problem angepasst. Durch das erzeugen der Objekte mit create bekomme ich keine Instanz um sie in einer Liste verwalten zu können. Deswegen habe ich die Basisklasse um eine getMe Methode erweitert. Das scheint ansich auch zu funktionieren, allerdings erzeugt der Aufruf der foo() Methode am Ende einen Speicherzugriffsfehler, den ich mir nicht erklären kann.

    Wie kann ich folgenden Code lauffähig machen, damit ich in meinem Programm alle Objekte in einer Liste ihres Basistyps speichern kann ?

    class A {
       public:
       virtual ~A() { }
       virtual void foo() { std::cout << 'A' << std::endl; }
       virtual A* getMe(){ return this; }
    };
    
     class B : public A{
       public:
       virtual ~B(){}
       virtual void foo() { std::cout << 'B' << std::endl; }
    };
    
    int main() {
       factory<std::string, A, smart_ptr_instantiator<A> > fct;
       fct.register_type<B>("B");
    
       A* a = fct.create("B")->getMe();
    
       a->foo();   // verursacht Speicherzugriffsfehler!!
    }
    


  • Mit smart_ptr_instantiator als instantiator_base_t gibt die Factory std::auto_ptrs zurück. Default sind einfache Zeiger, also geht das, was du willst, mit

    factory<std::string, A> fct;
    

    Allerdings musst du die Instanzen dann natürlich von Hand löschen.



  • Wunderbar...jetzt läuft wirklich alles wie es soll.. Danke nochmal


Log in to reply