Seite 5 von 6
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 18:33
von Krishty
Ach du Scheiße – dass Wachen keine Geräusche kennen würden ist natürlich Schwachsinn m[ Ich brauche dringend Schlaf.
Die anderen beiden müssten aber stimmen. Ich gucke DI nochmal drüber, was ich da geschrieben habe :)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 19:36
von CrystalCoder
Hm also eigentlich sieht man derartige Architekturen sehr oft im Einsatz. Gerade dann wenn man einem Skriptsystem erlaubt selbst Events zu verarbeiten (und zu versenden). Das ganze hat doch den Vorteil, dass man Abhängigkeiten soweit reduziert, dass jede Komponente kaum andere Komponenten kennen muss außer dem Eventsystem. Das ganze als Manager zu gestalten hat dann eben den Vorteil, dass dieser sich um das verteilen an alle registrierten Komponenten kümmert. Ich nenne das ja nicht Manager, weil ich nicht weiß wo ich es sonst zugehören würde, sondern weil es eben genau dem Zweck dient: Management.
Allerdings nützt es da wenig drüber zu streiten, denn es ist ja eigentlich deswegen sehr häufig im Einsatz, weil es sich bewährt hat. Es ist wie mit allem anderen: Wenn es nicht mehr ausreicht, wird sich was neues ausgedacht.
(Btw. würde ich das nicht als Singleton machen wollen, nicht dass es einer so verstanden hat)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 19:37
von dot
Chromanoid hat geschrieben:Naja dann müssen die Objekte ja irgendwie von einander wissen.
Ja aber hoffentlich doch.
Chromanoid hat geschrieben:Wie wäre es mit sowas: Eine Wache soll in Alarmbereitschaft gehen, wenn bestimmte Dinge im Spiel passieren. Zum Beispiel wenn eine Tür in gewisser Entfernung eingeschlagen wird. Jetzt will man ja nicht jeden Wachmann bei jeder Tür als Observer eintragen. Die Türen vermelden das Einschlagsereignis bei einem "gemeinsamen" Bekannten (der Wachmann braucht von seiner Bekanntheit nichts wissen)... Oder wie würdest du das lösen?
Auf jeden Fall würd ich das mal
innerhalb der Spiellogik lösen und nicht über einen globalen EventManager, der sämtliche Events sämtlicher Komponenten "managed". Und der "gemeinsame Bekannte" wäre auf keinen Fall ein EventManager, sondern irgendeines von beliebig vielen AlarmGuards-Entities in der Spiellogik. Außerdem halte ich den Observer-Pattern in diesem Beispiel von vornherein für unangebracht.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 19:38
von dot
CrystalCoder hat geschrieben:Das ganze hat doch den Vorteil, dass man Abhängigkeiten soweit reduziert, dass jede Komponente kaum andere Komponenten kennen muss außer dem Eventsystem.
Ist das so? Nur weil Abhängigkeiten unsichtbar werden, bedeutet das noch lange nicht, dass sie verschwinden. Solange die Abhängigkeiten aber explizit in den Schnittstellen auftreten, hast du grundsätzlich mal den Vorteil, dass du statisch-typsicher unterwegs bist und irgendwelche ungültigen Konstellationen schon gar nicht erst kompilieren. Mit deinem Eventsystem hast du nichts dergleichen.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 19:57
von CrystalCoder
Nur weil man Events abschickt müssen die noch lange nicht von einer Bestimmten Komponente abgearbeitet werden.
Jede Komponente kann selbst entscheiden welche Nachrichten diese abarbeitet und deswegen gibt es einfach keine direkte Abhängigkeit von Komponente A zu Komponente B
Und nur weil eine Komponente bestimmte Events akzeptiert (bzw, "abhört") heisst da auch nicht, dass diese überhaupt Events bekommt.
Dadurch dass man diese so entkoppelt können die Komponenten unabhängig voneinander entwickelt und getestet werden.
Die einzige Abhängigkeit, die dabei entsteht ist, dass alle gültigen Events vorher registriert werden müssen, bevor die entspr. Komponente diese verschickt, bzw. abhört.
Typsicher ist das ganze ebenfalls, weil das alles auf Schnittstellen basiert.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 20:09
von dot
CrystalCoder hat geschrieben:Nur weil man Events abschickt müssen die noch lange nicht von einer Bestimmten Komponente abgearbeitet werden.
Von was für Events reden wir da, wenn man nicht unbedingt will, dass sie von jemandem bearbeitet werden?
CrystalCoder hat geschrieben:Jede Komponente kann selbst entscheiden welche Nachrichten diese abarbeitet und deswegen gibt es einfach keine direkte Abhängigkeit von Komponente A zu Komponente B
Gibt mir mal ein Beispiel für so eine Nachricht.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 20:23
von CrystalCoder
dot hat geschrieben:CrystalCoder hat geschrieben:Nur weil man Events abschickt müssen die noch lange nicht von einer Bestimmten Komponente abgearbeitet werden.
Von was für Events reden wir da, wenn man nicht unbedingt will, dass sie von jemandem bearbeitet werden?
CrystalCoder hat geschrieben:Jede Komponente kann selbst entscheiden welche Nachrichten diese abarbeitet und deswegen gibt es einfach keine direkte Abhängigkeit von Komponente A zu Komponente B
Gibt mir mal ein Beispiel für so eine Nachricht.
Wenn die Entwicklung fertig ist, sollte man schon vermeiden dass sowas vorkommt, was jedoch nicht immer geht.
Aber angenommen ich hab eine Tür die wird getriggert (z.B. durch ein CollisionWithTriggerBox Event). Daraufhin startet ein Prozess der die Tür langsam schließt. Wenn die Tür zu ist (der Prozess also fertig ist), kann man ein Folgeevent "DoorClosed" oder so posten. Dieses Event kann das Skriptsystem aufgreifen und als Trigger verwenden, z.B. Monster spawnen lassen. Das könnte jetzt ewig so weitergehn. Gleichzeitig könnte man das Event zusätzlich vom Audiosystem lesen lassen und einen Soundtrack dazu abspielen lassen. (Und evtl dazu einen kurzen "Filmsequenz" Prozess starten, der das Spawnen der Monster in Szene setzt)
Andererseits muss dieses "DoorClosed" Event unbedingt verarbeitet werden -> Spieler geht in einen Raum, Tür geht hinter ihm zu und es passiert erstmal nichts weiter, bis die Tür wieder zum öffnen getriggert wird.
Wie man sieht hat man damit extrem viele Möglichkeiten und kann sowas sehr gut mit Skripten kombinieren (was auch gemacht wird). Es geht sogar soweit, dass man damit einen Großteil der Logik in Skripte packen kann, die dann von Leveleditoren erzeugt werden können.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:05
von dot
Gratuliere, so hast du deine Spiellogik implizit über das komplette System verteilt, anstatt sie in einer Komponente zu kapseln. Das ist imo eine wirklich schlechte Idee. Außerdem hast du nun nicht weniger Abhängigkeiten, sondern ein beträchtlich dichteres Netz an Abhängigkeiten. Denn z.B. deine Audiokomponente muss nun plötzlich wissen was ein DoorClosed Event ist. Sowas hat in einer Audiokomponente imo aber nun wirklich absolut nichts verloren.
EDIT: Ich find, dass ein Moderator das hier vielleicht mal in einen eigenen Thread verschieben sollte...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:29
von CrystalCoder
so hast du deine Spiellogik implizit über das komplette System verteilt
Ich hab mir das nicht ausgedacht.
Allerdings find ich das garnicht mal so schlimm für die Flexibilität die man dadurch bekommt.
EDIT: Ist nicht auf das gesamte System verteilt. Inwieweit das verteilt wird dafür ist ja der Manager zuständig. Er gibt jeder Komponente nur die Eventtypen, die sie abbonieren (via listener).
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:31
von dot
CrystalCoder hat geschrieben:Ich hab mir das nicht ausgedacht.
Wieso verwendest dus dann?
CrystalCoder hat geschrieben:Allerdings find ich das garnicht mal so schlimm für die Flexibilität die man dadurch bekommt.
Flexibilität? Mit so einem System erstickst du jegliche Flexibilität schon im Keim. Überleg doch mal: Nun willst du plötzlich eine neue Art von Tür, die keinen Ton macht, sondern z.B. irgendeinen speziellen Grafikeffekt. Auf einmal hast du deine Tür nun auch noch im Renderer. Und die Audiokomponente muss auf einmal noch wissen was für eine Art von Tür geöffnet wurde, um zu entscheiden, ob nun ein Sound gespielt werden soll oder nicht. Anstatt einfach eine neue Art von Tür in die Spiellogik einzubauen, musstest du den Code von 3 Komponenten verändern, die eigentlich nichts miteinander zu tun haben sollten...das ist doch völliger Mist!? Von der Tatsache, dass du Multithreading in so einem System komplett vergessen kannst, wollen wir lieber garnicht anfangen...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:44
von CrystalCoder
dot hat geschrieben:CrystalCoder hat geschrieben:Ich hab mir das nicht ausgedacht.
Wieso verwendest dus dann?
Ich verwende noch garnichts, so stehts im Buch beschrieben.
dot hat geschrieben:CrystalCoder hat geschrieben:Allerdings find ich das garnicht mal so schlimm für die Flexibilität die man dadurch bekommt.
Flexibilität? Mit so einem System erstickst du jegliche Flexibilität schon im Keim. Überleg doch mal: Nun willst du plötzlich eine neue Art von Tür, die keinen Ton macht, sondern z.B. irgendeinen speziellen Grafikeffekt. Auf einmal hast du deine Tür nun auch noch im Renderer. Und die Audiokomponente muss auf einmal noch wissen was für eine Art von Tür geöffnet wurde, um zu entscheiden, ob nun ein Sound gespielt werden soll oder nicht. Anstatt einfach eine neue Art von Tür in die Spiellogik einzubauen, musstest du den Code von 3 Komponenten verändern, die eigentlich nichts miteinander zu tun haben sollten...das ist doch völliger Mist!?
Das war nur ein Beispiel was man theoretisch machen könnte. Ich könnte stattdessen auch ein "PlayAudio" Event abschicken. Das Audiosystem sollte natürlich nur Audio Events erhalten.
Natürlich kann man das auch falsch anwenden. Das liegt dennoch im Bereich des Machbaren (über den Sinn kann man sich streiten).
Derartige Events, die das Gameplay betreffen sollten auch nur von der Gameplay Komponente verarbeitet werden.
Zu jeder Art von Event kommen übrigens noch weitere Infos dazu, nicht bloß der Name (im Fall von PlayAudio hätte man evtl ne RessourcenID + Abspielparameter).
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:50
von dot
CrystalCoder hat geschrieben:Ich könnte stattdessen auch ein "PlayAudio" Event abschicken. Das Audiosystem sollte natürlich nur Audio Events erhalten.
Dafür brauchst du aber kein Event, dafür könntest du einfach Audio->PlayAudio() aufrufen...
CrystalCoder hat geschrieben:Natürlich kann man das auch falsch anwenden. Das liegt dennoch im Bereich des Machbaren (über den Sinn kann man sich streiten).
Es geht doch gerade um den Sinn!? Machbar ist vieles. Dass es nicht machbar wäre, hab ich nie behautet. Ich versuche aufzuzeigen, warum es einfach nicht sinnvoll ist...
CrystalCoder hat geschrieben:Derartige Events, die das Gameplay betreffen sollten auch nur von der Gameplay Komponente verarbeitet werden.
Derartige Events sollten die Gameplay-Komponente von vornherein niemals verlassen. Also wieder kein Grund für einen EventManager...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 21:57
von CrystalCoder
dot hat geschrieben:CrystalCoder hat geschrieben:Ich könnte stattdessen auch ein "PlayAudio" Event abschicken. Das Audiosystem sollte natürlich nur Audio Events erhalten.
Dafür brauchst du aber kein Event, dafür könntest du einfach Audio->PlayAudio() aufrufen...
Und könnte das dann nicht schonmal ohne Sound testen wenn die Audiokomponente noch nicht fertig ist.
Abgesehen davon bräuchte ich dann eine Referenz auf das Audiosystem.
dot hat geschrieben:CrystalCoder hat geschrieben:Derartige Events, die das Gameplay betreffen sollten auch nur von der Gameplay Komponente verarbeitet werden.
Derartige Events sollten die Gameplay-Komponente niemals verlassen. Also wieder kein Grund für einen EventManager...
Wenn ich die GamePlay Komponente mit dem Skriptsystem verbinden will ist das dann aber wieder umständlicher als es sein kann.
Und damit vermeidet man direkte Abhängigkeiten, falls es doch dazu kommen sollte, dass man aus irgend einem Grund noch zusätzliche Komponenten brauchen sollte.
Man Spricht einfach eine neue Komponente via Event an und der Rest interessiert das Gameplay erstmal nicht. Was die neue Komponente damit macht ist dann ihre Sache.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 22:07
von dot
Ach, probiers doch einfach aus ;)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 22:07
von CrystalCoder
Hab ich vor gehabt. Ich stells mir ganz Spaßig vor :)
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 30.10.2011, 22:08
von dot
Spaß wirst du damit haben, soviel ist sicher :D
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 14:20
von BeRsErKeR
dot hat geschrieben:CrystalCoder hat geschrieben:Das ganze hat doch den Vorteil, dass man Abhängigkeiten soweit reduziert, dass jede Komponente kaum andere Komponenten kennen muss außer dem Eventsystem.
Ist das so? Nur weil Abhängigkeiten unsichtbar werden, bedeutet das noch lange nicht, dass sie verschwinden. Solange die Abhängigkeiten aber explizit in den Schnittstellen auftreten, hast du grundsätzlich mal den Vorteil, dass du statisch-typsicher unterwegs bist und irgendwelche ungültigen Konstellationen schon gar nicht erst kompilieren. Mit deinem Eventsystem hast du nichts dergleichen.
Irgendwie komisch. Beim Singleton prangerst du an, dass man viel Aufwand hat, wenn man doch mal was anders machen will und hier rätst du zu einer Lösung, die viel mehr Aufwand bei Änderungen erzeugen würde.
Stell dir mal ein GUI-System vor, in dem du Events durch Maus oder Tastatur auslöst. Willst du hier wirklich händisch irgendwelche Funktionen aufrufen? Das wird aber ein verdammt langer Code mit etlichen Verzweigungen und Aufrufen. Warum nicht einfach ein Event generieren, und die Controls entscheiden lassen ob sie es verarbeiten oder nicht? Wenn du keine einheitliche Event-Schnittstelle anbietest, sondern irgendwelche festverdrahteten Funktionen, dann bist du gezwungen beim Auslösen eines jeden Events das Zielobjekt komplett zu kennen. Noch weniger flexibel geht es wohl kaum. An der Stelle wo du Mausevents empfängst (z.B. in der WindowProc der WinAPI) musst du somit jedes GUI-Objekt kennen, das Maus-Events empfangen kann. Wie unterscheidest du hier GUI-Elemente, die keine Maus-Events verarbeiten können von solchen, die es können? Verschiedene Basisklassen? Das führt dann aber zwangsläufig in die Mehrfachvererbung. Ob das dann noch gutes Design ist, ich denke nicht.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 16:26
von dot
Ich glaub dir ist nicht ganz klar worums hier ging. So ein GUI-System braucht weder ein globales, zentrales, komponentenübergreifendes Event-Managementsystem, noch "langen Code voller Verzweigungen, Aufrufen, oder festen Verdrahtungen". Woher ich das weiß? Vielleicht liegts dran, dass ich gerade erst vor ein paar Monaten sowas implementiert hab (die längste Methode der Klasse GUI hat 52 Zeilen und liegt in einer cpp Datei, die keinen Header von auch nur einer einzigen konkreten Steuerelementklasse inkludiert).
Auch wenn der Observer-Pattern für Inputverarbeitung in einem GUI-System imo absolut keine gute Lösung ist, für die Anbindung der Business-Logic an das GUI sind Events unter Umständen manchmal ganz ok, aber dafür braucht es keinen "EventManager", erst recht keinen zentralen. Und genau darum gings in der letzten Diskussion hier. (Beispiel: Siehe irgendeine real-world Implementierung wie z.B. das Eventsystem von .NET)
Ich persönlich nehm in letzter Zeit aber auch dabei eher Abstand von Events, da sie eigentlich nur mit unnötigem Overhead verbunden sind. Eine einzelne Instanz eines Callback-Interface reicht imo völlig und forciert die klare Trennung von Logik und UI. Wann hängen denn schonmal wirklich mehrere Listener am selben Event? So eine Konstellation ist imo doch wieder nur Symptom von über alle möglichen, unzusammenhängenden Objekte verteiler Logik, Designfehler und daher zu vermeiden.
Mein GUI-System funktioniert in etwa so (ich hab mich beim Design ein wenig von WPF inspirieren lassen):
An der Wurzel steckt ein Objekt der Klasse GUI. Dieses Objekt bietet Methoden mouseUp(), mouseDown(), mouseMove() etc. an. Dies ist der einzige Weg auf dem Input in das System gelangt. Woher dieser Input genau kommt ist für das GUI an sich irrelevant. Wer auch immer das GUI instanziert, leitet den Input einfach direkt in das GUI hinein und fertig.
Im Kern des Systems gibt es zwei Datenstrukturen. Die an dieser Stelle interessante ist eine Baumstruktur (Composite-Pattern), die die räumliche und logische Beziehung zwischen den Steuerelementen modelliert. Diese Struktur bietet z.B. eine Methode hitTest(), welche es erlaubt zu einer gegebenen Position das jeweilige Steuerelement zu finden. Das GUI kann so von der Wurzel aus jeglichen Input zu jedem Zeitpunkt einem Steuerelement zuordnen. Der Baum muss natürlich nur durchsucht werden, wenn der Fokus sich verändert. Ansonsten fließt aller Input ohne Umweg direkt in das richtige Objekt. Die Verarbeitung von Eingaben erfordert somit kein komplexes herumreichen irgendwelcher Nachrichtenobjekte durch irgendwelche globalen (und dementsprechend gelockten) Listen an sämtliche Instanzen irgendwelcher Steuerelemente, die vielleicht daran interessiert sein könnten, am besten vielleicht noch verbunden mit Laufzeit-Typunterscheidungen (unterschiedliche Arten von Nachrichten besitzen ja schließlich auch unterschiedliche Arten von Parametern), Downcasts und durch mehrere Stufen verschiedenster Priority-Queues. Zu 99% der Zeit ist das einzige was gebraucht wird ein direkter und ein virtueller Methodenaufruf und das wars. Das System hört sich nicht nur simpel an, sondern ist auch simpel, sehr effizient, leicht zu Implementieren und zu warten (der ganze Kern des Systems passt in 400 Zeilen Standard-C++) und es kommt selbstverständlich mit jeder erdenklichen Art von Steuerelement zurecht, wobei die Implementierung neuer Steuerelemente natürgemäß genauso einfach ist, wie das System selbst. Man könnte die Inputverarbeitung noch aus dem logischen Baum herauslösen, was dann noch ermöglichen würde, dass Steuerelemente die keinen Input verarbeiten (z.B. irgendwelche Labels), von vornherein auch gar nie gefragt werden. In meinem konkreten Anwendungsfall hat sich das aber als unnötig erwiesen.
Der Vollständigkeit halber sei am Rande erwähnt, dass es sich bei der zweiten Datenstruktur im Prinzip um einen flachgedrückten Baum handelt, der dem möglichst schnellen Traversal beim Rendern dient. Aber für die Diskussion hier is das ja irrelevant.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 17:11
von BeRsErKeR
Um etwas globales bei einem System, dass "Events von außen" verarbeiten kann, wirst du niemals drum rum kommen, sofern du mit Fensterprozedur der WinAPI oder ähnlichem arbeitest.
Bei mir sind Event-Manager allerdings keineswegs global oder einzigartig, sondern vielmehr ein Interface. Programmbildschirme (Intro, Menü, Spiel, Outro, etc) sind z.B. ein Event-Manager, genauso wie das GUI. Das GUI kann deshalb trotzdem Teil eines anderen Event-Managers wie dem Spielbildschirm sein. Das Event-Manager-Interface bietet z.B. eine Methode an, die Events erhält und verarbeitet [bei mir OnEvent(Event &ev);].
Bei mir ist das GUI auch eine Art Baum, der alle Elemente kennt und die Events entsprechend "direkt" verteilt, die es von "außen" erhält. Das Spiel an sich kann aber auch Events erhalten (z.B. Mausklicks um den Spieler zu bewegen). Vorher gibt er das Event jedoch zunächst an das GUI weiter, sodass Klicks, die auf ein GUI-Element ggf. vorher abgefangen werden. Wurde ein Event verarbeitet wird es quittiert, sodass das Spiel in diesem Fall z.B. das Event nicht mehr intern auswertet, wenn das GUI das Event bereits verarbeitet hat.
Die Event-Manager bieten daher ein flexibles Interface und sind untereinander kompatibel. Dem Spiel ist z.B. egal an wen es das Event weiterleitet. An ein GUI oder einen anderen Event-Manager, etc. Man kann sogar die Event-Verarbeitung durch Austausch des Managers realisieren. Der WinAPI ist es egal an wen die Events gerade weitergegeben werden, an das Intro, das Spiel oder sonstwas.
Ich vermute einfach, dass wir hier unter Event-Manager verschiedene Dinge verstehen. Für mich ist es ein "Ding", welches Events managed, sprich bei Erhalt auswertet, verteilt, weiterleitet und/oder quittiert. Global muss sowas nicht sein, genausowenig wie andere Manager. Ob sie aber "bei gutem Design" so gut wie nie vorkommen möchte ich bezweifeln. Gutes Design per se gibt es sowieso nicht. Wichtig ist, dass man damit gut klar kommt und zum Ziel kommt. Sieh dir mal den QuakeIII Code an. Die Leute haben sich nicht mit solchen Sachen beschäftigt und haben dennoch effizienten und funktionierenden Code produziert. Ich finde man kann es auch übertreiben mit Aussagen wie "das und das ist gutes oder schlechtes Design". Nicht alles was man selbst gut oder schlecht findet ist dies auch für alle anderen, auch wenn man da 1000 Argumente zu vorbringen könnte.
Und zum Thema Overhead:
1. Übergibt man in der Regel eine Referenz oder einen Zeiger auf eine Event-Struktur.
2. Es hindert dich niemand daran, das Interface so zu gestalten, dass du für verschiedene Eventtypen eigene Empfangsmethoden definierst (z.B. OnMouseEvent(MouseEvent &ev); OnKeyEvent(KeyEvent &ev);) usw.
Und Typsicherheit? Wieso sollte die nicht gegeben sein? Du musst doch nicht den Weg der WinAPI mit WPARAM und LPARAM einschlagen oder unions nutzen. Ein Key-Down-Flag kannst du doch frei als bool anlegen und das bleibt es dann auch.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 17:45
von dot
BeRsErKeR hat geschrieben:Bei mir sind Event-Manager allerdings keineswegs global oder einzigartig, sondern vielmehr ein Interface. Programmbildschirme (Intro, Menü, Spiel, Outro, etc) sind z.B. ein Event-Manager, genauso wie das GUI. Das GUI kann deshalb trotzdem Teil eines anderen Event-Managers wie dem Spielbildschirm sein. Das Event-Manager-Interface bietet z.B. eine Methode an, die Events erhält und verarbeitet [bei mir OnEvent(Event &ev);].
Und wie entscheidet dein Event-Manager dann, um was für eine Art von Event es sich handelt und wie es zu bearbeiten ist?
BeRsErKeR hat geschrieben:Bei mir ist das GUI auch eine Art Baum, der alle Elemente kennt und die Events entsprechend "direkt" verteilt, die es von "außen" erhält. Das Spiel an sich kann aber auch Events erhalten (z.B. Mausklicks um den Spieler zu bewegen). Vorher gibt er das Event jedoch zunächst an das GUI weiter, sodass Klicks, die auf ein GUI-Element ggf. vorher abgefangen werden. Wurde ein Event verarbeitet wird es quittiert, sodass das Spiel in diesem Fall z.B. das Event nicht mehr intern auswertet, wenn das GUI das Event bereits verarbeitet hat.
Mausklicks im Spiel gehören imo zum Userinterface, weswegen das Spiel selbst sich bei mir in einem Steuerelement im GUI abspielt.
BeRsErKeR hat geschrieben:Die Event-Manager bieten daher ein flexibles Interface und sind untereinander kompatibel. Dem Spiel ist z.B. egal an wen es das Event weiterleitet. An ein GUI oder einen anderen Event-Manager, etc. Man kann sogar die Event-Verarbeitung durch Austausch des Managers realisieren. Der WinAPI ist es egal an wen die Events gerade weitergegeben werden, an das Intro, das Spiel oder sonstwas.
Meinem GUI ist es auch egal, was genau für Steuerelemente man reinsteckt.
BeRsErKeR hat geschrieben:Ich vermute einfach, dass wir hier unter Event-Manager verschiedene Dinge verstehen. Für mich ist es ein "Ding", welches Events managed, sprich bei Erhalt auswertet, verteilt, weiterleitet und/oder quittiert.
Ja, in etwa das versteh ich auch drunter.
BeRsErKeR hat geschrieben:Global muss sowas nicht sein, genausowenig wie andere Manager.
Genau darum gings hier aber hauptsächlich ;)
BeRsErKeR hat geschrieben:Ob sie aber "bei gutem Design" so gut wie nie vorkommen möchte ich bezweifeln. Gutes Design per se gibt es sowieso nicht. Wichtig ist, dass man damit gut klar kommt und zum Ziel kommt. [...]
Nicht alles was man selbst gut oder schlecht findet ist dies auch für alle anderen, auch wenn man da 1000 Argumente zu vorbringen könnte.
Ich nehme mir auch nicht heraus, die absolute Autorität darüber zu besitzen, zu entscheiden, was denn nun gutes Design ist. Ich hab eigentlich nur argumentiert, warum
ich XYZ für keine gute Idee halte und
imo bessere Alternativen aufgezeigt.
BeRsErKeR hat geschrieben:Sieh dir mal den QuakeIII Code an. Die Leute haben sich nicht mit solchen Sachen beschäftigt und haben dennoch effizienten und funktionierenden Code produziert. Ich finde man kann es auch übertreiben mit Aussagen wie "das und das ist gutes oder schlechtes Design".
Soweit ich das, nachdem ich meine Finger mal für ein paar Wochen im Quake III Code hatte, beurteilen kann, haben die Leute sich sehr sehr eingehend mit solchen Dingen beschäftigt. Und genau darum ist der Code effizient, flexibel und funktioniert.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 22:59
von BeRsErKeR
dot hat geschrieben:BeRsErKeR hat geschrieben:Bei mir sind Event-Manager allerdings keineswegs global oder einzigartig, sondern vielmehr ein Interface. Programmbildschirme (Intro, Menü, Spiel, Outro, etc) sind z.B. ein Event-Manager, genauso wie das GUI. Das GUI kann deshalb trotzdem Teil eines anderen Event-Managers wie dem Spielbildschirm sein. Das Event-Manager-Interface bietet z.B. eine Methode an, die Events erhält und verarbeitet [bei mir OnEvent(Event &ev);].
Und wie entscheidet dein Event-Manager dann, um was für eine Art von Event es sich handelt und wie es zu bearbeiten ist?
Er leitet die Events zunächst an all seine Kindelemente weiter. Falls keines der Kindelemente das Event verarbeitet hat, verarbeitet er es. Dabei kommt es auf den konkreten Event-Manager an was er tut. Das GUI würde eventuell gar nichts machen und das Event auf höherer Ebene weiterverarbeiten lassen, während ein Intro oder Outro bei einem Mausklick oder Tastendruck z.B. einfach übersprungen/beendet wird.
Die Art des Events ist im Event-Struct kodiert, z.B. über ein enum. Der Event-Manager fängt nur die Events ab, die er verarbeiten kann und wie gesagt leitet er sie vorher an die Childs weiter, welche auch nur die Events verarbeiten, die sie verarbeiten können.
dot hat geschrieben:BeRsErKeR hat geschrieben:Bei mir ist das GUI auch eine Art Baum, der alle Elemente kennt und die Events entsprechend "direkt" verteilt, die es von "außen" erhält. Das Spiel an sich kann aber auch Events erhalten (z.B. Mausklicks um den Spieler zu bewegen). Vorher gibt er das Event jedoch zunächst an das GUI weiter, sodass Klicks, die auf ein GUI-Element ggf. vorher abgefangen werden. Wurde ein Event verarbeitet wird es quittiert, sodass das Spiel in diesem Fall z.B. das Event nicht mehr intern auswertet, wenn das GUI das Event bereits verarbeitet hat.
Mausklicks im Spiel gehören imo zum Userinterface, weswegen das Spiel selbst sich bei mir in einem Steuerelement im GUI abspielt.
Das ist halt eine Frage des Designs. Bei mir sind GUI und Bildschirm getrennt. Ein Intro/Outro braucht in meinen Augen kein GUI (jedenfalls in meinem Fall nicht). Spielmenü und Ingame-Bildschirm besitzen ein GUI, allerdings als Member. Wie gesagt müssen wir da aber nicht drüber diskutieren, das macht halt jeder wie er möchte.
dot hat geschrieben:BeRsErKeR hat geschrieben:Sieh dir mal den QuakeIII Code an. Die Leute haben sich nicht mit solchen Sachen beschäftigt und haben dennoch effizienten und funktionierenden Code produziert. Ich finde man kann es auch übertreiben mit Aussagen wie "das und das ist gutes oder schlechtes Design".
Soweit ich das, nachdem ich meine Finger mal für ein paar Wochen im Quake III Code hatte, beurteilen kann, haben die Leute sich sehr sehr eingehend mit solchen Dingen beschäftigt. Und genau darum ist der Code effizient, flexibel und funktioniert.
Der Q3 Code ist nicht darauf ausgelegt ein hübsches Design zu haben, sondern effizient und schnell zu sein. Allerdings ist er vielleicht kein ganz so gutes Beispiel, weil er in C geschrieben ist und somit z.B. keine Klassen unterstützt.
Allerdings glaube ich, dass du nicht genug auf den Code geschaut hast. Da wurden riesige structs angelegt, die global zur Verfügung gestellt werden und wenn im Nachhinein was fehlte wurde einfach irgendwo was hinzugefügt oder Variablen zweckentfremdet. Im Prinzip würde man dafür heutzutage verprügelt werden, aber der Code läuft halt und ist auch recht flink. Wenn man da aber weiter dran entwickeln muss, z.B. für Mods, kommt man schnell an die Grenzen, weil er halt nicht gerade der flexibelste Code ist, den man sich vorstellen kann (und das ist die Untertreibung des Jahrhunderts ...).
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 31.10.2011, 23:29
von dot
BeRsErKeR hat geschrieben:Die Art des Events ist im Event-Struct kodiert, z.B. über ein enum. Der Event-Manager fängt nur die Events ab, die er verarbeiten kann und wie gesagt leitet er sie vorher an die Childs weiter, welche auch nur die Events verarbeiten, die sie verarbeiten können.
Also wie zu erwarten eben genau der übliche hässliche Typeswitch, wie ihn alle derartigen Systeme zwangsweise irgendwo aufweisen...
BeRsErKeR hat geschrieben:dot hat geschrieben:BeRsErKeR hat geschrieben:Sieh dir mal den QuakeIII Code an. Die Leute haben sich nicht mit solchen Sachen beschäftigt und haben dennoch effizienten und funktionierenden Code produziert. Ich finde man kann es auch übertreiben mit Aussagen wie "das und das ist gutes oder schlechtes Design".
Soweit ich das, nachdem ich meine Finger mal für ein paar Wochen im Quake III Code hatte, beurteilen kann, haben die Leute sich sehr sehr eingehend mit solchen Dingen beschäftigt. Und genau darum ist der Code effizient, flexibel und funktioniert.
Der Q3 Code ist nicht darauf ausgelegt ein hübsches Design zu haben, sondern effizient und schnell zu sein. Allerdings ist er vielleicht kein ganz so gutes Beispiel, weil er in C geschrieben ist und somit z.B. keine Klassen unterstützt.
Allerdings glaube ich, dass du nicht genug auf den Code geschaut hast. Da wurden riesige structs angelegt, die global zur Verfügung gestellt werden und wenn im Nachhinein was fehlte wurde einfach irgendwo was hinzugefügt oder Variablen zweckentfremdet. Im Prinzip würde man dafür heutzutage verprügelt werden, aber der Code läuft halt und ist auch recht flink. Wenn man da aber weiter dran entwickeln muss, z.B. für Mods, kommt man schnell an die Grenzen, weil er halt nicht gerade der flexibelste Code ist, den man sich vorstellen kann (und das ist die Untertreibung des Jahrhunderts ...).
Ja, der Code hat seine lokalen Hässlichkeiten, darum gings mir nicht, denn das ganze ist sowieso C und schon alt. Ich denk ich kenn einige der von dir erwähnten globalen structs besser als mir lieb ist ;)
Aber auch wenn der Code eben stellenweise grottig ist, so ist die Architektur an sich doch sehr weitsichtig und flexibel. Immerhin ist dieses Ding aus dem letzten Jahrtausend intern von Haus aus für Multi-Monitor, Stereo-3D und Multithreaded-Rendering ausgelegt. Und nicht nur drauf ausgelegt, all diese Dinge sind tatsächlich implementiert. Es gibt eine absolut strikte Trennung zwischen den Komponenten, die durchzusetzen sogar zusätzlicher Overhead in Kauf genommen wird. Das ganze Rendering läuft über ein "Shadersystem", das ohne viel Aufwand zu vollem GLSL Support aufgebohrt werden kann. Bedenkt man, dass der Code über 2 Jahre älter ist als die erste richtige GPU mit Shadersupport, so würde ich mal meinen, dass das Design doch sehr flexibel ist. Du wirst in der ganzen Engine nirgendwo sehen, dass Komponenten über irgendwelche schwammigen globalen Nachrichten kommunizieren ohne einander beim Namen zu nennen, oder dass irgendeine Form von Nachricht keinen klaren Empfänger hat oder gar von mehreren Komponenten frei nach deren Willen abgefangen wird. Der Implementierung sieht man an, dass sie im Laufe des Lebens an einigen Stellen "gewachsen" ist, fair enough. Aber eine Architektur wie die von Quake III entsteht garantiert nicht einfach so, ohne dass sich da jemand
sehr viele Gedanken über das Design gemacht hat.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 00:36
von BeRsErKeR
dot hat geschrieben:BeRsErKeR hat geschrieben:Die Art des Events ist im Event-Struct kodiert, z.B. über ein enum. Der Event-Manager fängt nur die Events ab, die er verarbeiten kann und wie gesagt leitet er sie vorher an die Childs weiter, welche auch nur die Events verarbeiten, die sie verarbeiten können.
Also wie zu erwarten eben genau der übliche hässliche Typeswitch, wie ihn alle derartigen Systeme zwangsweise irgendwo aufweisen...
Mich würde interessieren wie das ganze ohne jeglichen Switch gehen sollte. Ob du den Switch nun beim Generieren des Events durchführst und darauf basierend konkrete Schnittstellenmethoden aufrufst oder den Switch intern durchführst macht für mich keinen Unterschied. Allerdings finde ich letzteres weitaus flexibler als bei jeder Änderung (z.B. Hinzufügen einer Eventart), die Schnittstellen anzupassen. Dies ist mit Event-System nämlich nicht nötig. Man muss zwar das Switch nachpflegen, aber eben nur in den Komponenten, die das Event auch verarbeiten können und dort muss dann sowieso entsprechender Code hinzugefügt werden.
Ich frag mich sowieso wie du das ganze anstellst. Selbst wenn du weißt welches Objekt welche Events empfangen kann, so musst du ja irgendwo prüfen um welches Element es sich handelt, also handelst du dir da auch eine Art Switch ein (oder halt Verzweigungen). Du kannst ja nicht stupide MouseUp aufrufen, wenn das Objekt diese Methode gar nicht anbietet. Stell ich mir ehrlich gesagt wesentlich häßlicher vor.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 01:28
von dot
BeRsErKeR hat geschrieben:Mich würde interessieren wie das ganze ohne jeglichen Switch gehen sollte.
Ganz einfach: Indem ich meine Events nicht "managen" lasse. Wenn wirklich sowas wie Events, dann direkt nach dem Observer-Pattern ohne irgendwelche zentralen Mittelsmänner und Event-Objekte.
BeRsErKeR hat geschrieben:Ob du den Switch nun beim Generieren des Events durchführst und darauf basierend konkrete Schnittstellenmethoden aufrufst oder den Switch intern durchführst macht für mich keinen Unterschied. Allerdings finde ich letzteres weitaus flexibler als bei jeder Änderung (z.B. Hinzufügen einer Eventart), die Schnittstellen anzupassen. Dies ist mit Event-System nämlich nicht nötig. Man muss zwar das Switch nachpflegen, aber eben nur in den Komponenten, die das Event auch verarbeiten können und dort muss dann sowieso entsprechender Code hinzugefügt werden.
Es macht aber einen fundamentalen Unterschied. Beim "Generieren" des Events braucht man sowieso immer ein switch, weil das eben vom Betriebssystem her so vorgesehen ist. Den internen switch bräuchte man dann zusätzlich
überall, wo Events verarbeitet werden sollen.
Wo liegt das Problem die Schnittstellen anzupassen? Neu kompilieren muss man sowieso. Außerdem würde ich die Events, die eine Komponente verarbeitet, auch zu ihrer Schnittstelle zählen, immerhin ist das ja doch ein Weg, um mit der Komponente zu kommunizieren. Durch solche Events gibt's eben noch einen zusätzlichen, impliziten/verborgenen Teil der Schnittstelle, was wiederum einer meiner ersten Kritikpunkte an diesem System war. Denn wie gesagt: Die Abhängigkeiten verschwinden so nicht, sie werden so nur auf die gleiche Weise verschleiert, wie es z.B. auch bei globalen Variablen oder Singletons passiert. Kombiniert mit Faulheit/Bequemlichkeit/Unachtsamkeit führt das am Ende sehr schnell dazu, dass man sich, ohne es zu merken, in ein unnötig dichtes Netz an Abhängigkeiten verstrickt, aus dem man sich nur sehr schwer wieder befreien kann.
Es ging mir aber eigentlich garnicht wirklich um das switch Statement, sondern, rein auf konzeptioneller Ebene, um den Typeswitch an sich. Der Zweck des switch ist ja, von außen den konkreten Typ des Objektes zu erfahren und dann je nach Typ unterschiedliche Operationen auszuführen. Üblicherweise ist das wohl auch verbunden mit einem Downcast, ein weiteres Symptom des Designproblems. Sollte eine typabhängige Operation nicht eigentlich zum Typ gehören? Das Event selbst kennt seinen konkreten Typ doch sowieso!?
Die Tatsache, dass du das Objekt im Code an dieser Stelle nur allgemein als Event kennst, aber nun plötzlich den konkreten Typ des Events bestimmen musst, um eine Entscheidung zu treffen, zeigt, dass deine Abstraktion widersprüchlich ist. Denn dein Design sieht für diesen Code eigentlich genau das Gegenteil von dem vor, was der Code am Ende nun tut, nämlich dass
alle Events als eben solche
gleich behandelt werden.
Und das ist auch genau der Punkt, an dem du die statische Typsicherheit aufgibst.
BeRsErKeR hat geschrieben:Ich frag mich sowieso wie du das ganze anstellst. Selbst wenn du weißt welches Objekt welche Events empfangen kann, so musst du ja irgendwo prüfen um welches Element es sich handelt, also handelst du dir da auch eine Art Switch ein (oder halt Verzweigungen). Du kannst ja nicht stupide MouseUp aufrufen, wenn das Objekt diese Methode gar nicht anbietet. Stell ich mir ehrlich gesagt wesentlich häßlicher vor.
Die Situation, dass sowas wie MouseUp() irgendwo auf etwas aufgerufen werden würde, das diese Methode nicht bietet, kann in meinem System dank des Wunders der statischen Typsicherheit doch schon rein prinzipiell gar nie entstehen, sowas würde erst gar nicht kompilieren!?
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 01:53
von BeRsErKeR
dot hat geschrieben:BeRsErKeR hat geschrieben:Ich frag mich sowieso wie du das ganze anstellst. Selbst wenn du weißt welches Objekt welche Events empfangen kann, so musst du ja irgendwo prüfen um welches Element es sich handelt, also handelst du dir da auch eine Art Switch ein (oder halt Verzweigungen). Du kannst ja nicht stupide MouseUp aufrufen, wenn das Objekt diese Methode gar nicht anbietet. Stell ich mir ehrlich gesagt wesentlich häßlicher vor.
Die Situation, dass sowas wie MouseUp() irgendwo auf etwas aufgerufen werden würde, das diese Methode nicht bietet, kann in meinem System dank des Wunders der statischen Typsicherheit doch schon rein prinzipiell gar nie entstehen, sowas würde erst gar nicht kompilieren!?
Och Mensch, dass man hier immer genau auf die Formulierung achten muss, dass es richtig verstanden wird.
Dann halt anhand eines Beispiels:
Die Maus wird losgelassen und irgendwo stellst du das fest. Jetzt hast du deinen tollen GUI-Baum und ggf. benötigt irgendein GUI-Objekt in diesem Baum das "Event". Du sagst nun, dass dein Root (GUI) das "Event" zum Zielort weiterreichst, oder? Wie kommt es dort hin und wie gewährleistest du, dass das Zielobjekt überhaupt das "Event" verarbeiten kann bzw. woher weißt du das?
Soweit ich verstanden habe muss das Objekt dann eine Methode MouseUp anbieten. Aber wie stellst du das fest?
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 01:57
von dot
Der für den Input zuständige Baum besteht von doch von vornherein schon nur aus Objekten, die auch Input verarbeiten können!?
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 01:58
von BeRsErKeR
Sprich jedes Objekt des Baums kann jedes Input-Event verarbeiten?
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 02:00
von dot
Ja, ganze 5 sinds...
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 02:04
von dot
Wobei das jetzt etwas vereinfacht war. Eigentlich besteht der Baum aus Controls, welche auf Anfrage ein Widget-Interface liefern, welches dann die Mausinteraktion abhandelt (mouseUp/Down/Move/Wheel sowie Drag&Drop). Keyboardinput unterscheidet sich ja logisch von Mausinput und würde dementsprechend auch getrennt behandelt.
Re: [C++] Speicherfreigabe bei statischem Singleton
Verfasst: 01.11.2011, 02:06
von BeRsErKeR
Hmm in meinem GUI soll aber z.B. nicht jedes Element auch jedes Event unterstützen. Für manche brauche ich z.B. keine KeyEvents usw. Warum sollte mein Objekt in dem Fall also eine Schnittstelle dafür anbieten? Wenn ich deinen Ansatz verfolgen würde bräuchte ich ganz unterschiedliche Objekte mit unterschiedlichen Interfaces und dann muss ich auch wieder Prüfungen anstellen und im schlimmsten Fall noch zu dynamic_casts greifen, bevor ich da was aufrufen kann. Also das finde ich dann nicht so prickelnd.