Dynamisches Generieren von Klassen mit Makro- oder Template-Programmierung



  • @nwp3: Danke, das Prinzip ist mir jetzt klar!



  • Wobei ich immer noch nicht sehe, was hier das Template bringt. Das Gleiche kann man mit normaler Vererbung erreichen.

    Oder ist das Problem ernsthaft die 2 Zeilen Code für die Klassendefinition?



  • Ich denke mit klassischer Vererbung würde man vermutlich prinzipiell genauso weit kommen. In meinem Fall ist die Anzahl der Kind-Instanzen jedoch "unbekannt" (variabel wäre wohl passender) im Sinne von mal sind es nur 2, dann 4, dann 10 usw.

    D.h. ich muss in der Lage sein, die Kind-Klassen quasi "dynamisch" VOR der Laufzeit zur generieren. Würde man statt den Template-Ansatz die klassische Vererbung nehmen müsste jedes Mal der Code angepasst werden. .

    Zumindest ist soweit mein Verständnis der ganzen Sache ...

    Klar, man muss jede Kind-Klassen Methode doKind() individuell implementieren aber das ist für mich ok.



  • Das macht immer noch keinen Sinn. Ein Template bringt dir hier keine zusätzliche Variabilität. Du musst genauso jede Methode einzeln implementieren, wie wenn du direkt erben würdest. Bei der Vererbung hast du zudem die Möglichkeit, sinnvolle Klassennamen statt nur Instantiierungen mit Zahlen zu wählen.

    Mit anderen Worten: Du hast rein gar nichts automatisiert.



  • Dann erkläre mir bitte wie Du mit einer Variablen Anzahl von benötigten Kind-Klassen umgehen würdest.

    class Eltern {
    
    	public:
    	Eltern() {}
    	void doEltern() {}
    };
    
    class Kind1 : public Eltern {
    
    	public:
    	Kind1() : Eltern() {}
    	void doKind() {}
    };
    
    class Kind2 : public Eltern {
    
    	public:
    	Kind2() : Eltern() {}
    	void doKind() {}
    };
    
    ...
    
    class KindN : public Eltern {
    
    	public:
    	KindN() : Eltern() {}
    	void doKind() {}
    };
    

    D.h. ich muss in der Lage sein, die Kind-Klassen quasi "dynamisch" VOR der Laufzeit zur generieren. Würde man statt den Template-Ansatz die klassische Vererbung nehmen müsste jedes Mal der Code angepasst werden. .

    Ein solches Vorgehen ist für mich nicht praktikabel, daher finde ich den aus Deiner Sicht nicht automatisierten Ansatz besser. Das die doKind()-Methoden individuell angepasst werden müssen stellt für mich kein Problem dar und ist im Moment noch hinnehmbar. Natürlich wäre es schicker den kompletten Code generieren zu lassen, aber das ist im Moment einfach unnötig.



  • nicnoc schrieb:

    Dann erkläre mir bitte wie Du mit einer Variablen Anzahl von benötigten Kind-Klassen umgehen würdest.

    Die ist nicht variabel, sondern zur Compilezeit bekannt.
    Da kann man sie genauso gut von Hand schreiben.
    Reden wir mal anders:
    Was ist die Eltern-Klasse genau und was sollen die einzelnen Kind Klassen machen?*
    Vielleicht würden wir das ja ganz anders lösen.

    *Edit: Yeah, konsistente Bentzung von Bindestrichen.



  • Beschreib mal wodrum es wirklich geht. (Nicht dass das ein XY-Problem ist)

    Denn, wenn du N Kindklassen brauchst (mit N verschiedenen Implementierungen), du dir aber "nur" die N Klassendefinitionen per Makro/Template schreibst, hast du ungefähr 5 Minuten Tipparbeit durch x Stunden Gesuche und Probiere ersetzt, was dir letztlich mehr als nichts gebracht hat.

    Was ich mir vorstellen könnte wäre, dass du M Kindklassen implementierst, aber dir ein Makro oder ein Template so baust, dass N Kindklassen genutzt werden (mit N < M). Aber auch da ist die Frage wofür?!

    Wofür braucht man eine unbestimmte Anzahl an Klassen vor dem Kompilieren (Templates und Metaprogramming mal ausgenommen ;)), die dann auch noch verschiedene Implementierungen haben?



  • Es geht hier generell um Systemtasks. Die Eltern-Klasse entspricht der Task-Klasse und die Kinder der jeweiligen Task-Spezialisierung. Genauer gesagt entspricht die Kind-Methode doKind() dem jeweiligen Doing der entsprechenden Taskspezialisierung.

    Pesudocode:

    void Eltern::doEltern() {
    
        createTask(func,this);
    }
    
    void Eltern::func(void* param) {
    
        static_cast<Eltern *>(param)->doKind();
    }
    
    void main() {
    
        /* Kind aka. Task wird angelegt */
        Kindx kindx(name, alter);
        /* doEltern erstellt einen Systemtask und ruft über die C-Funktion
        createTask() die Methode func() mit "this" als Übergabeparameter auf und
        startet abhängig von der Kind-Klasse bzw. Task-Klasse die doKind()-Methode*/
        kindx.doEltern();
    }
    

    Da ich jetzt pseudo automatisiert die spezialisierten Task-Klassen erzeugen kann ist der Ansatz von nwp3 für mich ausreichend.

    Allerdings habe ich jetzt bei dem static_cast() das Problem, dass jetzt offensichtlich keine genaue Zuordnung zwischen Kind-Klasse und der Methode doKind() vorhanden ist und eine beliebige doKind()-Methode aufgerufen wird.

    Nachtrag: Das Problem mit dem static_cast habe ich nicht wenn ich die klassische Vererbung verwende.



  • Sorry Guys, ich hab da nurn dickes Fragezeichen nach mehrmaligen Lesen übern Kopf oO 😕



  • Was spricht gegen:

    class task
    {
    private:
      std::function<void(void)> fnc_; // oder function-pointer/template wenn kein c++11
    
      static void helper(void *data)
      {
      	 static_cast<task*>(data)->fnc_();
      }
    
    public:
    	task(std::function<void(void)> fnc)
    	: fnc_(std::move(fnc)) {}
    
    	void do_task()
    	{
    		create_task(helper, this);
    	}
    };
    
    int main()
    {
    	task a(function_pointer), b(functor()), c([](){std::cout << "Hallo Welt!"});
    	a.do_task();
    	b.do_task();
    	c.do_task();
    }
    

    (Hab create_task einfach so übernommen ohne zu wissen, was es genau tut.)


  • Mod

    @Nathan: helper sollte wohl static sein.



  • Arcoth schrieb:

    @Nathan: helper sollte wohl static sein.

    Ups, ja sollte es.


  • Mod

    Lässt sich noch ein wenig vereinfachen:

    class task : std::function<void()>
    {
    public:
    
    	using std::function<void()>::function;
    
    	void do_task()
    	{
    		create_task( [](task* p){ (*p)(); }, this );
    	}
    };
    

Anmelden zum Antworten