Designfrage: Anpassbare Codestellen zusammenfassen oder dokumentieren?
-
Dravere schrieb:
Kannst du vielleicht das Problem etwas näher beleuchten, also worum es hier genauer geht?
Konkret geht es um eine Simulation verschiedener Objekte, die miteinander interagieren. Ich könnte mir vorstellen, dass andere Benutzer später neue Objekte hinzufügen werden, die dann wiederum auf eine gewisse Art miteinander interagieren. Ich erwarte aber nicht, dass das grundlegende Framework für die Simulation sich ändern wird.
Ich habe das Interface bisher nach meinem besten Wissen abstrakt gehalten. Da es bei der Sache aber schon ziemlich auf Performance ankommt, wollte ich rechenintensive Techniken, wie eine Virtualisierungsschicht zur Laufzeit, vermeiden. Irgendwo muss früher oder später in den Code eingegriffen werden, um neue Klassen und deren Verhalten bekannt zu machen.
Konkret müsste man folgende Komponenten ändern, um etwas hinzuzufügen:
- Das Interface für die Objektklasse implementieren
- Die neue Klasse in die Liste der zu verwendenden Objekttypen eintragen
- Der Interaktionsmatrix das neue Objekt und die Interaktionen zugehörigen hinzufügen
- Die neuen Interaktionen implementierenDas sind vom Aufbau des Simulators her eigentlich getrennt Bereiche, aber ich könnte natürlich diese Teile auch in eine einzelne Datei schreiben.
Und Danke auch an die anderen Meinungen im Thread, auch wenn sie sich widersprechen, vielleicht wird mit diesen Zusatzinfos das Bild ja eindeutiger.
-
SeppJ schrieb:
Dravere schrieb:
Kannst du vielleicht das Problem etwas näher beleuchten, also worum es hier genauer geht?
Konkret geht es um eine Simulation verschiedener Objekte, die miteinander interagieren. Ich könnte mir vorstellen, dass andere Benutzer später neue Objekte hinzufügen werden, die dann wiederum auf eine gewisse Art miteinander interagieren. Ich erwarte aber nicht, dass das grundlegende Framework für die Simulation sich ändern wird.
Ich habe das Interface bisher nach meinem besten Wissen abstrakt gehalten. Da es bei der Sache aber schon ziemlich auf Performance ankommt, wollte ich rechenintensive Techniken, wie eine Virtualisierungsschicht zur Laufzeit, vermeiden. Irgendwo muss früher oder später in den Code eingegriffen werden, um neue Klassen und deren Verhalten bekannt zu machen.
Konkret müsste man folgende Komponenten ändern, um etwas hinzuzufügen:
- Das Interface für die Objektklasse implementieren
- Die neue Klasse in die Liste der zu verwendenden Objekttypen eintragen
- Der Interaktionsmatrix das neue Objekt und die Interaktionen zugehörigen hinzufügen
- Die neuen Interaktionen implementierenDas sind vom Aufbau des Simulators her eigentlich getrennt Bereiche, aber ich könnte natürlich diese Teile auch in eine einzelne Datei schreiben.
Und Danke auch an die anderen Meinungen im Thread, auch wenn sie sich widersprechen, vielleicht wird mit diesen Zusatzinfos das Bild ja eindeutiger.
Wenn es dir so auf Performance ankommt, kannst du nicht einfach eine Scriptsprache verwenden, die am Anfang alles initialisiert und dann damit arbeitet?
-
CodeDown schrieb:
Wenn es dir so auf Performance ankommt, kannst du nicht einfach eine Scriptsprache verwenden, die am Anfang alles initialisiert und dann damit arbeitet?
Ich verstehe nicht, was du damit meinst, kannst du das erläutern?
-
Ich erkläre mal an einem einfachen Beispiel, wie es für mich anhört, was du machen willst und wie ich es lösen würde:
Du hast Einheiten (class Unit), wie zum Beispiel einen Schwertkämpfer (class Swordsman : public Unit) oder Bogenschützen (class Bowman : public Unit). Wenn du es so aufbaust, wie ich es in den Klammern hingeschrieben habe, dann musst du tatsächlich immer eine eigene Klasse für jede neue Einheit implementieren. Viel einfacher wäre es doch, wenn du nur die KlasseUnithättest, welche alle benötigten Eigenschaften hat. Über zum Beispiel ein XML File könnte man nun seine Einheiten definieren. Diese werden alsUnitObjekte angelegt und registriert. So kann man einfach über XML die Objekte verändern. Geladen und initialisiert wird zu Begin, wodurch kein Performanceverlust zur Laufzeit passiert.Für unterschiedliche Algorithmen wären da durchaus Skriptsprachen von Vorteil. Du kompilierst oder übersetzt die Skriptsprache einfach zu Begin und hälst sie dann in einer effektiven Form. Ähnlich wie es auch gewisse JITs machen, zum Beispiel bei C#. Es kommt da halt ein wenig auf den Umfang an. Vielleicht hilft es auch nur, wenn man die Eigenschaften selber einstellen kann und keine eigenen Algorithmen definieren muss.
Du könntest dem Benutzer der Bibliothek natürlich auch die Möglichkeit geben, Algorithmen in C++ zu definieren und sie dann als Name dem Parser übergeben. Damit wäre es möglich aus dem XML File heraus einfach nur der Name des Algorithmus zu definieren, wodurch dieser über Funktionszeiger oder ähnliches aufgerufen wird.
Ich hoffe, dass ich ein paar Ansätze geben konnte, welche Richtungen es hier geben könnte.
Grüssli
-
Dravere schrieb:
Ich erkläre mal an einem einfachen Beispiel, wie es für mich anhört, was du machen willst und wie ich es lösen würde:
Du hast Einheiten (class Unit), wie zum Beispiel einen Schwertkämpfer (class Swordsman : public Unit) oder Bogenschützen (class Bowman : public Unit). Wenn du es so aufbaust, wie ich es in den Klammern hingeschrieben habe, dann musst du tatsächlich immer eine eigene Klasse für jede neue Einheit implementieren. Viel einfacher wäre es doch, wenn du nur die KlasseUnithättest, welche alle benötigten Eigenschaften hat. Über zum Beispiel ein XML File könnte man nun seine Einheiten definieren. Diese werden alsUnitObjekte angelegt und registriert. So kann man einfach über XML die Objekte verändern. Geladen und initialisiert wird zu Begin, wodurch kein Performanceverlust zur Laufzeit passiert.Das klingt nach einer guten Idee. Momentan mache ich es mit meinen eigenen Objekten schon ähnlich (aber umständlicher), da muss ich nur wenig ändern um das zu verbessern.
Für unterschiedliche Algorithmen wären da durchaus Skriptsprachen von Vorteil. Du kompilierst oder übersetzt die Skriptsprache einfach zu Begin und hälst sie dann in einer effektiven Form. Ähnlich wie es auch gewisse JITs machen, zum Beispiel bei C#. Es kommt da halt ein wenig auf den Umfang an. Vielleicht hilft es auch nur, wenn man die Eigenschaften selber einstellen kann und keine eigenen Algorithmen definieren muss.
Ich glaube das Erzeugen von gutem Maschinencode geht weit über meine Kenntnisse hinaus, vor allem da das ganze plattformunabhängig sein muss. Wenn ich bedenke, viele Entwicklerjahrhunderte beispielsweise Java gebraucht hat bis der JIT-Compiler einigermaßen flott war, kann ich da als Einzelner wohl nicht mithalten. Oder habe ich dich hier falsch verstanden?
Du könntest dem Benutzer der Bibliothek natürlich auch die Möglichkeit geben, Algorithmen in C++ zu definieren und sie dann als Name dem Parser übergeben. Damit wäre es möglich aus dem XML File heraus einfach nur der Name des Algorithmus zu definieren, wodurch dieser über Funktionszeiger oder ähnliches aufgerufen wird.
Das klingt schon besser umsetzbar, ich werde mal prüfen, wieviel Aufwand das wäre.
-
SeppJ schrieb:
Ich glaube das Erzeugen von gutem Maschinencode geht weit über meine Kenntnisse hinaus, vor allem da das ganze plattformunabhängig sein muss. Wenn ich bedenke, viele Entwicklerjahrhunderte beispielsweise Java gebraucht hat bis der JIT-Compiler einigermaßen flott war, kann ich da als Einzelner wohl nicht mithalten. Oder habe ich dich hier falsch verstanden?
Du musst ja nicht gleich Maschinencode erzeugen

Aber du kannst die Skriptsprache parsen und daraus einen Ablaufplan erstellen und diesen etwas optimieren. Da ist schon vieles mit relativ wenig Mitteln möglich. Vor allem wenn du die Skriptsprache nicht zu komplex machst und vielleicht gibt es dafür bereits schon fertig Bibliotheken.Es geht halt vor allem darum, dass du nicht jedes Mal das Skript neu parst, wieder interpretierst und ausführst. Am besten das Result von parsen und interpretieren, gegebenenfalls leicht optimieren, abspeichern und dann jeweils nur noch ausführen.
Grüssli
-
Ahh, verstehe. Ja, so eine Skriptsprache wäre tatsächlich innerhalb meiner Möglichkeiten, ich schätze ich würde mich dadurch aber zu sehr einschränken. Um nochmal das Spielbeispiel zu verwenden: Derzeit mögen meine Einheiten zwar noch Schwertkämpfer und Bogenschütze sein und ich könnte mir denken eine Skriptsprache zu entwickeln die beide beschreibt aber auch Katapulte und Düsenjäger erlaubt. Es könnte bei mir aber auch eines Tages als Einheit Telefone, Philosophie oder die Farbe Blau hinzukommen. Ich habe jetzt schon so ein Objekt dabei, das in gar kein Schema passt (bis auf wenige Eigenschaften) und an (fast) jeder Stelle eine Sonderbehandlung benötigt. Ich fürchte ich brauche für neue Einheiten wirklich die Mächtigkeit einer ausgewachsenen Programmiersprache.
-
SeppJ schrieb:
Es könnte bei mir aber auch eines Tages als Einheit Telefone, Philosophie oder die Farbe Blau hinzukommen.

Ehm, also irgendwo muss es doch gemeinsame Nenner geben, sonst kann es deine Engine gar nicht behandeln

Naja, da kann ich grundsätzlich jetzt nur noch als zusätzlicher Tipp geben: Habe keine Angst vor virutellen Funktionen und Funktionszeiger wegen der Performance. Die kann man in dem Fall getrost vernachlässigen. Es gilt hier das 80-20 Prinzip. 80% der Zeit wird in 20% des Codes verbracht. Zudem optimiere erst richtig, wenn du mit einem Profiler drüber bist. Vorausüberlegungen schaden natürlich nicht, aber wenn man dann anfängt virtuelle Funktionen im Voraus auszuschliessen, dann geht man definitiv zu weit
Grüssli
-
Dravere schrieb:
SeppJ schrieb:
Es könnte bei mir aber auch eines Tages als Einheit Telefone, Philosophie oder die Farbe Blau hinzukommen.

Ehm, also irgendwo muss es doch gemeinsame Nenner geben, sonst kann es deine Engine gar nicht behandeln

Naja, da kann ich grundsätzlich jetzt nur noch als zusätzlicher Tipp geben: Habe keine Angst vor virutellen Funktionen und Funktionszeiger wegen der Performance. Die kann man in dem Fall getrost vernachlässigen. Es gilt hier das 80-20 Prinzip. 80% der Zeit wird in 20% des Codes verbracht. Zudem optimiere erst richtig, wenn du mit einem Profiler drüber bist. Vorausüberlegungen schaden natürlich nicht, aber wenn man dann anfängt virtuelle Funktionen im Voraus auszuschliessen, dann geht man definitiv zu weit
Grüssli
Warum muss ich nur an zwei Worte denken, die man hier nicht schreiben darf, weil sonst Volkard alles löscht.
-
Dravere schrieb:
SeppJ schrieb:
Es könnte bei mir aber auch eines Tages als Einheit Telefone, Philosophie oder die Farbe Blau hinzukommen.

Ehm, also irgendwo muss es doch gemeinsame Nenner geben, sonst kann es deine Engine gar nicht behandeln

Naja, da kann ich grundsätzlich jetzt nur noch als zusätzlicher Tipp geben: Habe keine Angst vor virutellen Funktionen und Funktionszeiger wegen der Performance. Die kann man in dem Fall getrost vernachlässigen. Es gilt hier das 80-20 Prinzip. 80% der Zeit wird in 20% des Codes verbracht. Zudem optimiere erst richtig, wenn du mit einem Profiler drüber bist. Vorausüberlegungen schaden natürlich nicht, aber wenn man dann anfängt virtuelle Funktionen im Voraus auszuschliessen, dann geht man definitiv zu weit
Grüssli
Ja, war vielleicht ein bisschen übertrieben :D, natürlich gibt es einen gemeinsamen Nenner, an manchen Stellen sind bei einigen Sachen aber tatsächlich Sonderbehandlungen angesagt. Insofern tendiere ich auch eher zu der zweiten Möglichkeit bei meiner Ursprungsfrage, weil manchmal dann doch an unvorhergesehenen Stellen eingegriffen werden muss.
Ich werde das mit der Performance der virtuellen Funktionen noch mal am halbfertigen Projekt testen, damit würde einiges einfacher sein. Ich hatte es im vorhinein schon mal probiert, aber meine Testfunktionen hatten deutlich weniger Anweisungen als die fertigen Funktionen, insofern war der Overhead relativ größer und die Performance entsprechend schwach. Ich hatte mich damals aus diesem Grund gegen virtuelle Funktionen entschieden.