vector mit Interface Klasse
-
Mechanics schrieb:
Man könnte z.B. eine virtuelle Methode itemClass einführen, die einen string zurückgibt und den zum matchen benutzen. Die Funktion könnte man dann vielleicht auch an anderen Stellen verwenden. Man könnte auch IDs verwenden, die irgendwann über den Klassennamen registriert werden (ich denk da an sowas wie Q_DECLARE_METATYPE).
Was willst du mit der Information anfangen, wenn du nicht casten willst?
-
mgaeckler schrieb:
Designziel war, daß der Klassenbaum erweiterbar sein sollte, ohne, daß die Wurzel geändert werden muß.
Dann kannste gleich Zeiger auf void nehmen. Das Designziel würde ich zuerst überdenken.
Also ein Hauptfenster kriegt Nachrichten udn verteilt sie an alle EMpfänger, die diese Nachrichtensorte verarbeiten können? Ginge auch pro Nachrichtensorte ein Slot?
-
seldon schrieb:
Du hast bislang nur beschrieben, wie du es machst, nicht, was du damit erreichen willst. In der vorgelegten Form kann ich dir nur sagen, dass dein Design hoffnungslos kaputt ist, aber nicht, wodurch man es ersetzen könnte. Aber ich mach mal ein bisschen Kaffeesatzleserei.
Wenn es nur um den Aufbau von Popup-Menüs geht, sollte die Sache einfach zu regeln sein; immerhin braucht eine Action ja nur eine parameterlose Methode (mehr als anklicken kann man einen Eintrag nicht). Halte einen std::vector<std::function<void()>> menu_entries (oder std::pair<std::string, std::function<void()>> für eine Beschreibung, oder was auch immer du zum Rendern brauchst) und wirf da Binder rein; dann kennen die Binder den konkreten Typ des Kontextobjekts, und du musst das Zeug nicht mehr aus einem Container rauskramen, der sich die notwendigen Informationen nicht gemerkt hat.
Ja ganz so einfach ist es aber nicht. Grundsätzlich habe ich ja so eine Liste. Es muß aber einen Weg geben, mit dem entschieden werden kann, ob jetzt eine konkretet Funktion mit dem konkreten Objekt überhaupt möglich ist. In dem Container könnten verschiedene Objekte enthalten sein. Jedes dieser Objekte kann, muß aber nicht, eigene Einträge in dem Popupmenu enthalten, es könnte sogar möglich sein, daß ein Objekt einen Popupeintrag erhält, von dem es sogar selbst keine Ahnung hat. Das Hauptfenster hat daher einfach eine Liste aller überhaupt möglichen Einträge des Popupmenüs.
So wird die Liste der Einträge eines Popupmenüs aufgebaut:void getItemActions( const USER_OR_GROUP *actUser, THE_ITEM *theItem, Array<ACTION_BASE *> *actions ) { actions->clear(); for( size_t i=0; i<allActions->getNumElements(); i++ ) { ACTION_BASE *theAction = (*allActions)[i]; if( theAction->acceptUser( actUser ) && theAction->acceptItem( theItem ) ) actions->addElement( theAction ); } actions->sort( actionCompare ); }mfg Martin
-
mgaeckler schrieb:
Ja ganz so einfach ist es aber nicht. Grundsätzlich habe ich ja so eine Liste. Es muß aber einen Weg geben, mit dem entschieden werden kann, ob jetzt eine konkretet Funktion mit dem konkreten Objekt überhaupt möglich ist.
Aber muss die Funktion das wissen? Kann das Objekt nicht die Entscheidung treffen, ob es mit der Funktion spielen mag?
-
volkard schrieb:
Schwachsinn. Da helfe ich Dir für 500€/Tag (Vorkasse) raus.
Nein danke, Deine Qualifikation reicht dafür leider nicht aus. Nachdem Du hier wohl auch eine Moderatorenrolle übernommen hast, solltest Du vielleicht wenigstens diesen Artikel lesen: http://de.wikipedia.org/wiki/Moderation_(Gruppenarbeit)
-
mgaeckler schrieb:
volkard schrieb:
Schwachsinn. Da helfe ich Dir für 500€/Tag (Vorkasse) raus.
Nein danke, Deine Qualifikation reicht dafür leider nicht aus. Nachdem Du hier wohl auch eine Moderatorenrolle übernommen hast, solltest Du vielleicht wenigstens diesen Artikel lesen: http://de.wikipedia.org/wiki/Moderation_(Gruppenarbeit)
Bisher war mein Beitrag hier nicht in der Rolle als Mod.
Aber da Du Wert drauf legst, lese ich mal nach.
http://de.wikipedia.org/wiki/Moderator_(Beruf)#Moderator_.28Internet.29Ok, unerwünscht.
Laß das Trollen mit Deinem vermurksten Code und den daraus abgeleiteten Schlussfolgerungen. Du bringst den Neulingen damit Mist bei und senkst die Qualität des Forums. Weitere Beiträge dahingehend werde ich löschen.
-
Mechanics schrieb:
Wieso schreibst du Klassennamen komplett groß und mit Unterstrichen? Das ist ja echt hässlich.
Hab ich mir mal so angewöhnt. Gefällt mir aber auch nicht mehr. Neuere Klassen bekommen jetzt einen schönderen Namen.
Mechanics schrieb:
Man kann u.U. schon dynamic_cast verwenden, ich schließe das nicht so kategorisch aus wie die anderen hier. Nur sollte man das wenn nur irgendwie möglich vermeiden und erst recht nicht als Teil des Designs benutzen.
Dynamic casts sind noch teurer wie virtuelle Funktionen, daher sind sie nur dann anzuwenden, wenn es erforderlich ist.
Mechanics schrieb:
Man könnte z.B. eine virtuelle Methode itemClass einführen, die einen string zurückgibt und den zum matchen benutzen. Die Funktion könnte man dann vielleicht auch an anderen Stellen verwenden. Man könnte auch IDs verwenden, die irgendwann über den Klassennamen registriert werden (ich denk da an sowas wie Q_DECLARE_METATYPE).
Daran habe ich auch schon gedacht, ist aber nicht besser, wie dynamic_cast. Das Problem dabei nämlich ist, wenn der Entwickler hier irgendein Fehler macht, merkt er das erst zur Laufeit und muß u.U. aufwendig mit 'nen Debugger suchen. Nach 'nen dynamic cast muß ich nur prüfen, ob ich NULL zurückbekommen habe, was wesentlich einfacher ist. Beispiel:
Ich habe eine Klasse THE_FOLDER, die keine verschiedenen Versionen haben kann. Hätte ich in meinem ursprünglichen Beispiel versehentlioch statt 'nen dynamic_cast auf THE_FILE einen auf THE_FOLDER gemacht, hätte sich spätestens beim Versuch canCreateVersion von THE_FOLDER der Compiler beschwert. Würde ich stattdessen einen Pattern-vergleich oder ID-Vergleich oder was auch immer machen und den Zeiger mit 'nem static_cast in die richtige Form bringen, um canCreateVersion aufrufen zu können. dann würde es irgendwanneinmal zur Laufzeit krachen. Ich kann mir nicht vorstellen, daß einer von Euch das besser findet.
mfg Martin
-
volkard schrieb:
mgaeckler schrieb:
Ja ganz so einfach ist es aber nicht. Grundsätzlich habe ich ja so eine Liste. Es muß aber einen Weg geben, mit dem entschieden werden kann, ob jetzt eine konkretet Funktion mit dem konkreten Objekt überhaupt möglich ist.
Aber muss die Funktion das wissen? Kann das Objekt nicht die Entscheidung treffen, ob es mit der Funktion spielen mag?
Dazu muß das Objekt aber die Funktion kennen. Das ist aber nicht gewährleistet.
mfg Martin
-
Mir scheint, es ist nur double dispatching, was Du baust.
-
volkard schrieb:
Mir scheint, es ist nur double dispatching, was Du baust.
Nein darum geht es nicht. Das ist was anderes. Auch wenn die Probleme ähnlich sind. Beim double dispatching geht es darum eine passende Methode in Abhängigkeit von mehr als einem Objekttyp aufzurufen. Hier habe ich aber nur ein Objekt. Die Actionklassen dienen ausschließlich dem Dispatchmechanismus haben selbst aber keine einzige Membervariable. Es ging einfach darum, daß es möglich sein mußte, das System zu erweitern ohne auch nur eine einzige Zeile im vorhandenen Quelltext zu ändern. Einfach ein neues Modul hinzubinden, fertig.
Mit der jetzigen Implementierung kann ich eine abgeleitete Klasse von THE_FILE implementieren und alle ACTIONS, die THE_FILE mögen, können auch mit der neuen Klasse, weil der dynamic_cast erfolgreich sein wird. Ich könnte aber auch eine neue Funktion z.b. Copy To Web oder so einführen, die alle Dateien auf 'nen Webserver kopieren kann, ohne THE_FILE ändern zu müssen.
mfg Martin
-
Beim Arbeiten mit Syntaxbäumen hatte ich häufig die Verlegenheit wissen zu müssen welcher konkrete Knoten sich hinter einem generalisierten Ausdrucksknoten befindet. Ganz unnötig finde ich Downcasting zur Laufzeit nicht.

-
Ethon schrieb:
Beim Arbeiten mit Syntaxbäumen hatte ich häufig die Verlegenheit wissen zu müssen welcher konkrete Knoten sich hinter einem generalisierten Ausdrucksknoten befindet. Ganz unnötig finde ich Downcasting zur Laufzeit nicht.

Dann muß Du aber schon prüfen, ob eine andere Methode nicht besser ist. Bedenke, der dynamic_cast ist recht aufwendig. Insbesonders, wenn der Klassenbaum stark in die Tiefe geht, also viele Hierarchieebenen hat.
mfg Martin
-
mgaeckler schrieb:
Ethon schrieb:
Beim Arbeiten mit Syntaxbäumen hatte ich häufig die Verlegenheit wissen zu müssen welcher konkrete Knoten sich hinter einem generalisierten Ausdrucksknoten befindet. Ganz unnötig finde ich Downcasting zur Laufzeit nicht.

Dann muß Du aber schon prüfen, ob eine andere Methode nicht besser ist. Bedenke, der dynamic_cast ist recht aufwendig. Insbesonders, wenn der Klassenbaum stark in die Tiefe geht, also viele Hierarchieebenen hat.
mfg Martin
Muss ja nicht dynamic_cast sein. Enum + static_cast kommt ja auf das Selbe heraus, aus Design-Perspektive, ist aber schneller.
-
Ethon schrieb:
Muss ja nicht dynamic_cast sein. Enum + static_cast kommt ja auf das Selbe heraus, aus Design-Perspektive, ist aber schneller.
OK. Da die "hitzige" Diskussion hier um dynamic casts ging, nahm ich an, daß Du für Deinen Syntaxbaum auch diesen benutzen willst. So kannst Du meinen Beitrag natürlich ignorieren.

mfg Martin
-
mgaeckler schrieb:
Ethon schrieb:
Muss ja nicht dynamic_cast sein. Enum + static_cast kommt ja auf das Selbe heraus, aus Design-Perspektive, ist aber schneller.
OK. Da die "hitzige" Diskussion hier um dynamic casts ging
Nee, um Downcasts im Allgemeinen.
Also, wenn ich dein Problem richtig verstehe:
Du hast also eine Klasse, die eine Aktion durchführt (ACTION_BASE), abgeleitete Klassen davon implementieren dann jeweils die Aktion.
Diese Aktion wird auf einem Item durchgeführt (THE_ITEM), abgeleitete Klassen davon implementieren das Item an sich.
ACTION_BASE hat nun u.a. eine pure virtual Funktion acceptItem, die checkt, ob ein gegebenes THE_ITEM gültig ist, um mit ihm die Aktion durchzuführen.
Beispiele für Aktion ist ACTION_ADD_VERSION, die arbeitet nur mit dem Item THE_FILE.
So weit richtig?Das Grundproblem hierbei ist, dass entweder Aktionen oder Items für acceptItem ein Type-Switch durchführen müssen.
In deinem Fall ist der in den Aktionen über dynamic_cast von Items implementiert, könnte aber genauso gut in Item über dynamic_cast von Aktionen implementiert sein.
Das grundsätzliche Problem hierbei ist der Type-Switch. Type-Switches sind schlecht, da sie eine gewisse Anzahl an Klassen hardcoden.
Die Lösung dafür sind idR virtuelle Funktionen.
Das Problem ist jedoch, dass die virtuelle Funktion in Abhängigkeit von zwei Objekten sein muss.
Im Endeffekt hatte volkard also Recht: du brauchst double-dispatching.
-
Nathan schrieb:
Im Endeffekt hatte volkard also Recht: du brauchst double-dispatching.
Ich würde es anders ausdrücken. Volkard hatte nicht recht. Er braucht nicht double-dispatching, sondern er macht es schon. Es gibt sehr viele Wege, double-dispatching zu bewirken, insbesondere nicht nur den einen Weg, den Wikipedia aufzeigt. Ich nehme an, er könnte locker O(1) hinkriegen, wenn er dynamic_cast miede.
-
Ich weiß nicht, was ihr alle habts. Ich finde den Anwendungsfall von mgaeckler eigentlich klar und sowas kommt auch häufiger vor.
Denkt mal an einen Feature Baum im CAD. Es gibt einen Baum, in dem verschiedene Operationen und Objekte dargestellt werden. z.B. ein Objekt Skizze und eine Ebene drunter die Operation Extrudieren. Die haben teilweise unterschiedliche Kontextmenü Einträge. Sowohl die Elemente im Baum als auch die Kontextmenü Einträge sollten sich nicht ALLE gegenseitig kennen und beide sind durch Plugins erweiterbar. Es ist ok, wenn ein Plugin einen Kontextmenü Eintrag hinzufügen will, der für Extrusionen gültig ist und auf die Extrusionsparameter zugreifen muss. Das Plugin kennt das Extrusionsobjekt, aber nicht umgekehrt.
Enums sind hier auch nicht optimal, weil ein Plugin könnte z.B. auch weitere Knotentypen für den Baum definieren, z.B. spezielle Wiederholmuster. Von dem her würde man zumindest einen Namen des Typs als String brauchen.
Ich seh hier auch nicht wirklich ein Problem mit dem dynamic_cast. Die Performance ist an der Stelle völlig egal, das Kontextmenü kann man wahrscheinlich zig tausend mal pro Sekunde aufbauen, ohne dass der Benutzer das merkt, das sollte hier echt kein Argument sein.
Ist halt die Frage, ob man das viel besser implementieren kann. dynamic_cast wäre zumindest ein gangbarer Weg, ohne das Gesamtsystem um irgendwelche zusätzlichen Funktionen zu erweitern, von dem her kann das durchaus ok sein. So wie ich mir das vorstelle, hätt ich wahrscheinlich eh von vornherein eine Funktion wie typeName eingebaut und dann einen static_cast gemacht, wenn der Name dem erwarteten entspricht.
Man könnte die Infos auch vorher sammeln. Vielleicht eine Klasse MenuManager, die alle Knoten und alle Aktionen kennt. Die Knoten haben eine Methode typeName, die Aktionen eine Methode validForTypes. Dann könnte der MenuHandler eine entsprechende Map aufbauen und nur die gültigen Aktionen für einen Knoten anzeigen. Hilft aber auch nicht immer weiter, weil vielleicht will man ja weiter Eigenschaften von dem Knoten prüfen, z.B. ob eine Komponente aus einem PDM ausgecheckt wurde oder nicht.
-
Nathan schrieb:
mgaeckler schrieb:
Ethon schrieb:
Muss ja nicht dynamic_cast sein. Enum + static_cast kommt ja auf das Selbe heraus, aus Design-Perspektive, ist aber schneller.
OK. Da die "hitzige" Diskussion hier um dynamic casts ging
Nee, um Downcasts im Allgemeinen.
Also, wenn ich dein Problem richtig verstehe:
Du hast also eine Klasse, die eine Aktion durchführt (ACTION_BASE), abgeleitete Klassen davon implementieren dann jeweils die Aktion.
Diese Aktion wird auf einem Item durchgeführt (THE_ITEM), abgeleitete Klassen davon implementieren das Item an sich.
ACTION_BASE hat nun u.a. eine pure virtual Funktion acceptItem, die checkt, ob ein gegebenes THE_ITEM gültig ist, um mit ihm die Aktion durchzuführen.
Beispiele für Aktion ist ACTION_ADD_VERSION, die arbeitet nur mit dem Item THE_FILE.
So weit richtig?Perfekt erkannt.

Nathan schrieb:
Das Grundproblem hierbei ist, dass entweder Aktionen oder Items für acceptItem ein Type-Switch durchführen müssen.
In deinem Fall ist der in den Aktionen über dynamic_cast von Items implementiert, könnte aber genauso gut in Item über dynamic_cast von Aktionen implementiert sein.
Das grundsätzliche Problem hierbei ist der Type-Switch. Type-Switches sind schlecht, da sie eine gewisse Anzahl an Klassen hardcoden.
Die Lösung dafür sind idR virtuelle Funktionen.
Das Problem ist jedoch, dass die virtuelle Funktion in Abhängigkeit von zwei Objekten sein muss.
Im Endeffekt hatte volkard also Recht: du brauchst double-dispatching.Nur die üblichen Vorgehensweisen beim double dispatch setzen voraus, daß sich die beteiligten Objekte gegenseitig kennen. Das ist aber hier leider nicht gegegeben. Auch bekommt die das ACTION-Objekt nur einen BASIS-Zeiger. Klar kann man das auch z.B. mit irgendwelchen Namenskonventionen, wie bereits vorgeschlagen, lösen. Ich habe aber schon ausführlich erläutert, warum ich dies nicht für die optimale Lösung halte.
mfg Martin
-
volkard schrieb:
Nathan schrieb:
Im Endeffekt hatte volkard also Recht: du brauchst double-dispatching.
Ich würde es anders ausdrücken. Volkard hatte nicht recht. Er braucht nicht double-dispatching, sondern er macht es schon. Es gibt sehr viele Wege, double-dispatching zu bewirken, insbesondere nicht nur den einen Weg, den Wikipedia aufzeigt. Ich nehme an, er könnte locker O(1) hinkriegen, wenn er dynamic_cast miede.
Das mag schon sein, daß man mit einer anderen Implementierung den einen oder anderen Taktzyklus einsparen kann. Aber mechanics hat es schon richtig erkannt, es geht hier um Nano- oder Microsekunden Ersparnis auf Kosten von Wartbarkeit. Dem Anwender wird es egal sein, ob der Aufbau des Popupmenus nun 0,05 s oder 0,051 s dauert.
Ich ziehe bei meinen Klassendesigns i.d.R. die Wartbarkeit und leichte Erweiterbarbeit der Performance vor. Neulich mußte ich aber eine Funkltionalität optimieren, die ganze 7 Tage durchlief. Da habe ich natürlich alle Register gezogen, die das System erlaubte, um das ganze zu beschleunigen. Man sollte auf Performance achten, wenn es geboten ist, auf Wartbarkeit jedoch wo es möglich ist. Programmierfehler sind viel teuer, wie die eine oder andere Nanosekunde längere Laufzeit.
Ich kann mich noch gut erinnern, vor 35 Jahren haben wir bei unseren Implementierungen auch jeden Taktzyklus zusammen addiert und um jeden gefeilscht, den wir loswerden konnten. Ich bin froh, daß das heutzutage nicht mehr so erforderlich ist, auch wenn es extrem nervig ist, daß viele Anwendungen heute auch nicht schneller sind, wie vor 30 Jahren, obwohl die Hardware um den Faktor 1000000 schneller geworden ist.
mfg Martin
-
mgaeckler schrieb:
Nur die üblichen Vorgehensweisen beim double dispatch setzen voraus, daß sich die beteiligten Objekte gegenseitig kennen. Das ist aber hier leider nicht gegegeben.
Also einfach nur kein "Mehr Effektiv C++ programmieren" gelesen.
*heul*