Klasse an Funktion übergeben. Und auch wieder zurück.



  • Hi Leute,
    Ich hab mir grad was ausgedacht, aber hab keine Ahnung, ob das tut.
    Es geht um eine Classfactory. Momentan registrier ich dort eine ID und eine statische Funktion: (Alles Pseudocode)

    Factory->Register(id,class::static_create_function);
    

    Wenn ich dann ein neues Objekt will:

    class* Factory::Create(id)
    {
        return registered_functions[id]();
    }
    

    So, meine Frage ist jetzt, ob das ganze auch mit Klassen funktioniert:

    Factory->Register(id,class);
    

    Wenn ich dann ein neues Objekt will:

    class* Factory::Create(id)
    {
        return registered_classes[id]::static_create_function();
    }
    

    Sinn ist der, dass die Klassen, die ich registriert habe, statische Funktionen haben. Um diese Funktionen zu benutzen brauch ich logischerweise keine Instanz der Klassen. Also wieso eine anlegen?



  • Was ich nicht verstehe, wenn deine Klassen doch alle eine Methode haben um sich selbst zu erzeugen, wozu dann die Factory? Geht es dir nur darum, dass du id zur Laufzeit in einen Klassen-Typ übersetzen kannst?

    Mit Klassen direkt funktioniert es übrigens nicht, du könntest aber Funktionspointer speichern.



  • Da Funktionsparameter non-type sein müssen, geht das nicht so, wie du dir das vorstellst (oder mir fällt zumindest momentan nix ein). Aber du kannst dir mit Templates behelfen. Mir fällt da folgender Ansatz ein (Pseudocode):

    template <class T>
    struct type_to_type
    {
    };
    
    template <class T>
    T* Factory::Create(const type_to_type<T>& helper)
    { 
    	return T::static_create_function();
    }
    
    class* objekt = Factory->Create(type_to_type<class>());
    // ungetestet
    

    Du musst zwar eine Instanz von type_to_type erstellen, ein ordentlicher Compiler sollte das aber wegoptimieren.



  • Whoops, passt wohl doch besser in diesen Thread. Ich hab da mal was geschrieben, was dich interessieren könnte: http://www.dev-geeks.org/forum/viewtopic.php?t=110

    Die Technik, die ich da benutze, basiert auf einer Art Metafunktion. Ich hab eine Hilfsklasse

    struct basic_factory_helper { virtual value_type *create_instance() = 0; };
    

    ...und eine davon abgeleitete template, die ich für jede Klasse, die die Factory verwalten soll, konkretisiere:

    template<typename _t> struct factory_helper : basic_factory_helper {
      virtual value_type *create_instance() { return pointer(new _t()); }
    };
    

    ...wobei value_type ein Parameter der factory, die sinnigerweise auch templatisiert ist, und pointer ein value_type* ist. Auf diese Art kann ich nachher in einer std::map<key_type, basic_factory_helper*> factory_helpers für alle registrierten Klassen speichern und bei Bedarf darauf zugreifen. Die Registrierung geht dann über die template-Methode

    template<typename _t_reg> void register_class(key_type const &k);
    

    ...also z.B. mit

    factory<std::string, Base>::register_class<Derived>("Derived");
    

    Der restliche Code wrappt dieses Prinzip in ein Singleton und stellt ein paar convenience-Funktionen zur Verfügung, damit man nicht immer factory<key, value>::instance().create("foo"); schreiben muss...



  • und was ist mit klassenregistrierung zur laufzeit? wie siehts da mit deinen templates aus?
    static packt das viel besser



  • Was soll damit sein? Natürlich werden die Klassen erst zur Laufzeit registriert.



  • Wenn ich das richtig sehe, erfüllt das aber auch nicht meine Anforderungen ...
    Ich kann zwar eine Klasse übergeben (statt einer Funktion), kann sie mir aber nicht zurückgeben und somit auch nicht auf beliebige statische Funktionen selbiger zugreifen.



  • Mit statischen Methoden geht das auch nicht so ohne weiteres. Du könntest dir höchstens ein riesiges switch-statement über die typeid basteln, aber dafür müsstest du vorher alle Klassen kennen, und wirklich schön wärs auch nicht grad. Warum verbieten sich denn virtuelle Funktionen so sehr?



  • Sie verbieten sich nicht wirklich, aber mit statischen wärs einfach schöner gewesen. Folgendes Konstrukt dürfte auch nciht funktionieren, oder?

    class Factory
    {
    public:
        Execute(Key,Func,Par1,Par2)
        {
            Base *Instance =  CreateInstance(Key);
            Instance->Func(Par1,Par2);
            delete Instance;
        }
    };
    

    Dann habe ich faktisch zwar eine Instanz, die ist aber für den User nicht sichtbar, er muss sich also nicht um ihre Verwaltung kümmern



  • Jedenfalls nicht so, wie du es dir vorstellst. Aber für sowas gibt es ja std::auto_ptr. Schau dir mal das Beispiel an, das ich zu meinem Code geschrieben habe, da benutz ich std::auto_ptr, um die Instanz zu kapseln, damit ich mich nicht mehr selbst um das delete kümmern muss.



  • Hallo!
    Ich versuche gerade das Prinzip dieser ClassFactory zu verstehen, aber irgendwie steh ich ganz schön heftig auf der Leitung 😞 Kann es mir mal jemand in Worte fassen? Wozu braucht man zum Beispiel die Klasse basic_factory_helper, wenn die einzig darin vorhandene Funktion sowieso überschrieben wird? Eine Registrierung einer Klasse sieht ja so aus:
    static fku::factory<std::string, A>::register_t<B> reg_b("B");
    Kann mir das Stück Code mal jemand erklären!? Wozu muss bei factory beim Template am Anfang string angegeben werden und dann die Klasse A? Muss da noch die Basisklasse angegeben werden? Wozu? Wäre nett, wenn mir jemand mal das ganze Prinzip in Worte fassen könnte... 😞

    Danke,
    Bis Dann,
    Kevin



  • ich hab mir auch irgendwann mal eine art classfactory aufgebaut, die in etwa so funktionierte:

    class base{...};//hiervon müssen alle klassen die von der factory erstellt werden können erben
    
    class A:public base{...};//eine einfache beispielklasse
    base* createClass(){//eine funktion um die klasse zu erstellen
        return new A;
    }
    //in der main
    factory a;
    a.registerClass("irgendeinName",&createClass);
    A* b=a.create("irgendeinName");
    

    //edit aus dem kopf verschreibsler^^



  • Die basic_factory_helper ist dazu da, dass man das ganze in ner map speicher kann, da eine factory_helper<int> was anderes als ne factory_helper<string> ist benötigt man die gemeinsame basisklasse. Der String ist der Key, der mit in der map gespeichert wird.

    @otze: Das ist ja was ganz anderes ...



  • mir viel grad was ein, die idee ist noch nicht ausgereift und ich hab keine ahnung ob das überhaupt umsetzbar ist, aber vielleicht könnt ihr damit weiterarbeiten, dies ist nur eine art pseudocode:

    class base{};
    template<class T>
    struct derived:public base{
        typedef T valueType;    
    };
    template<class T>void registerClass(std::string a){
        map.insert(pair<base*,std::string>(new derived<T>,a);
    }
    void execute( std::string b){
     *(map.find(b)->second)::valueType::static_func();
    }
    


  • Ich weiss zwar nicht, was das soll, aber falls du den Thread gelesen hättest, wüsstest du, dass das alles schon hier steht ...



  • war halt nur ein lösungsansatz um an den klassentyp selber zu kommen, bisher wurde das ja eher über funktionen versucht, mein lösungansatz ging über typedefs(und der ansatz wurde hier noch nicht genannt 🙄 )



  • Und was ist das dann?

    0xdeadbeef schrieb:

    Whoops, passt wohl doch besser in diesen Thread. Ich hab da mal was geschrieben, was dich interessieren könnte: http://www.dev-geeks.org/forum/viewtopic.php?t=110

    Die Technik, die ich da benutze, basiert auf einer Art Metafunktion. Ich hab eine Hilfsklasse

    struct basic_factory_helper { virtual value_type *create_instance() = 0; };
    

    ...und eine davon abgeleitete template, die ich für jede Klasse, die die Factory verwalten soll, konkretisiere:

    template<typename _t> struct factory_helper : basic_factory_helper {
      virtual value_type *create_instance() { return pointer(new _t()); }
    };
    

    ...wobei value_type ein Parameter der factory, die sinnigerweise auch templatisiert ist, und pointer ein value_type* ist. Auf diese Art kann ich nachher in einer std::map<key_type, basic_factory_helper*> factory_helpers für alle registrierten Klassen speichern und bei Bedarf darauf zugreifen. Die Registrierung geht dann über die template-Methode

    template<typename _t_reg> void register_class(key_type const &k);
    

    ...also z.B. mit

    factory<std::string, Base>::register_class<Derived>("Derived");
    

    Der restliche Code wrappt dieses Prinzip in ein Singleton und stellt ein paar convenience-Funktionen zur Verfügung, damit man nicht immer factory<key, value>::instance().create("foo"); schreiben muss...

    Beiträge sind übrigens nicht so böse gemeint, wie sie sich anhören 😉



  • Und was ist das dann?

    ein anderer ansatz mit ähnlichem interface und einer instanz der klasse, mit dem zwangsläufigen nachteil, dass man keine statischen funktionen von den gespeicherten klassen aufrufen kann?

    aber was weis ich schon^^

    //edit böser rechtschreibfehler kusch, verschwinde 😃



  • Die Instanz gibt's aber erst, wenn man ein create_instance aufruft.
    Wenn man die factory_helper_base und die factory_helper ein wenig abändert, kann man auch ohne probleme statische Funktionen aufrufen:

    struct basic_factory_helper
    {
        virtual value_type *create_instance() = 0;
        virtual void execute()=0;
    };
    
    template<typename _t> struct factory_helper : basic_factory_helper
    {
        virtual value_type *create_instance()
        {
            return pointer(new _t());
        }
        virtual void execute()
        {
            _t::static_function();
        }
    };
    
    void execute( std::string b){
     map.find(b)->second->execute();
    }
    

    Aber ich hab mir deins grad erst richtig angeschaut, muss mal prüfen, ob das net doch was anderes ist 😉



  • @otze: Das mit den typedefs wird nicht hinhauen, weil die bereits zur Compilezeit aufgelöst werden.

    @deUS: Hm. OK, das kann man so machen. Allerdings ist die factory dann auf Klassen beschränkt, die die entsprechenden statischen Funktionen mitliefern. Für meinen Geschmack nicht sonderlich schön, aber über Geschmack kann man bekanntlich lange und ergebnislos streiten. Funktionieren sollte es allemal.


Anmelden zum Antworten