alternative für RTTI
-
hallo
ich bin grade dabei mir eine gui zu schreiben. alle elemente haben eine
gemeinsame basisklasse "element". ich speichere in jedem übergeordnetem
element wie z.b. einem fenster ein vector mit zeigern auf die untergeordneten
elemente wie z.b. ein knopf.über virtuelle funktionen wird jeweils die richtige funktion ausgewählt, das ist
auch kein problem. das tritt erst auf wenn man über die schnittstelle
elementspezifische daten eines elementes ändern will. den nicht jedes element
hat die selben daten.jedes element bekommt bei der erzeugung eine einheitliche id zugewiesen,
und über die bekommt man dann auch den zeiger auf das richtige element.da die id statisch ist, gibt es keine probleme. allerdings muss man dazu den
zeiger casten. das ist natürlich schlechtes designbeispielcode
class element { public: element *getelement(unsinged id); private: std::vector<element *> childelements; }; class window : public element { }; class button : public element { }; // ... gui *g = new gui(...) window *w = g->addwindow(..., 1742); w->addbutton(..., 8765); //... reinterpret_cast<button *>(g->getelement(1742)->getelement(8765))->settext(...);
dynamic_cast oder rtti möchte ich dabei gerne vermeiden.
auch das implementieren sämtlicher methoden in der basisklasse
ein container pro klasse würde zwar gehen aber würde irgentwann unübersichtlich.wie könnte man das eleganter lösen?
MfG fragezeichen?
-
fragezeichen? schrieb:
dynamic_cast oder rtti möchte ich dabei gerne vermeiden.
Warum?
-
fragezeichen? schrieb:
reinterpret_cast<button *>(g->getelement(1742)->getelement(8765))->settext(...);
magste
g->getelementwindow(1742)->getelementbutton(8765))->settext(...);
?
-
audacia schrieb:
fragezeichen? schrieb:
dynamic_cast oder rtti möchte ich dabei gerne vermeiden.
Warum?
weil man trotzdem casten muss und dynamic_cast für (geringe) performance-
probleme bekannt istvolkard schrieb:
magste
g->getelementwindow(1742)->getelementbutton(8765))->settext(...);
?
das wäre schon ganz schön, nur ist die methode settext nicht bei allen
abgeleiteten klassen vorhanden. natürlich könnte man den ganzen
satz funktionen in der basisklasse implementieren, aber das würde dann
schon ein bisschen viel. winapi machts ja auch so.im moment sieht es so aus als ob jede klasse einen eigenen container bekommt,
womit ich aber das getelement in der basisklasse vergessen kann...was auch noch möglich wäre, sind zwischenklassen, die alle childelemente
einer klasse zusammenfassen. dann stellt sich allerdings das problem wie
eine progressbar und ein editfeld in eine klasse passen sollen.
-
fragezeichen? schrieb:
dynamic_cast für (geringe) performance-probleme bekannt ist
Gegenüber dem, was dem cast dann folgt, ist das aber ein Witz
Ich finde sowas in der Art ganz angenehm:
class ChildWindow { public: // Lauter Sachen, die für alle sinnvoll sind: void show(); void hide(); // ... }; class Checkbox : public ChildWindow { public: // Checkbox-spezifische Sachen: bool isChecked(); }; if ( main_wnd.get<Checkbox>(12345).isChecked() ) // ...
-
fragezeichen? schrieb:
volkard schrieb:
magste
g->getelementwindow(1742)->getelementbutton(8765))->settext(...);
?
das wäre schon ganz schön, nur ist die methode settext nicht bei allen
abgeleiteten klassen vorhanden.aber bei allen buttons. und getelementbutton gibt ja nen Button* zurück.
im moment sieht es so aus als ob jede klasse einen eigenen container bekommt, womit ich aber das getelement in der basisklasse vergessen kann...
ich dachte nie an eigene container pro klasse.
idee
g->getelement<Window>(1742)->getelement<Button>(8765))->settext(...);
ok, ich geb's zu: das ist nur ne andere schreibweise für
g->dynamic_cast<Window&>(getelement(1742)).dynamic_cast<Button&>(getelement(1765)).settext(...);
-
Badestrand schrieb:
fragezeichen? schrieb:
dynamic_cast für (geringe) performance-probleme bekannt ist
Gegenüber dem, was dem cast dann folgt, ist das aber ein Witz
Also auf 64-Bit Windowssystemen können dynamic_casts ein Problem sein, vorallem wenn man ein paar 100000 pro Sekunde macht.
http://blogs.msdn.com/junfeng/archive/2006/10/17/dynamic-cast-is-slow-in-x64.aspx
http://archives.windowshpc.net/blogs/dev_gen/archive/2006/10/17/647.aspx
Das wird doch unseren premature optimization meister freuen. :pfragezeichen? schrieb:
jedes element bekommt bei der erzeugung eine einheitliche id zugewiesen,
und über die bekommt man dann auch den zeiger auf das richtige element.Wozu dieses ID Zeugs? Warum erzeugst du nicht einfach einen Button und übergibst den dann, dann hast du schon den Pointer auf den Button und musst ihn nicht wieder holen?
-
volkard schrieb:
fragezeichen? schrieb:
volkard schrieb:
magste
g->getelementwindow(1742)->getelementbutton(8765))->settext(...);
?
das wäre schon ganz schön, nur ist die methode settext nicht bei allen
abgeleiteten klassen vorhanden.aber bei allen buttons. und getelementbutton gibt ja nen Button* zurück.
sry hab das button überlesen
sämmtliche template-methoden verstecken den dynamic_cast nur.
bei einer 60fps anwendung mit einem vollen menü was auf mehreren seiten
knöpfchen und schalter hat wird das schon ein bisschen viel.außerdem mag ich den cast nicht so. das die möglichkeit besteht dass der
zurückgegebene zeiger nicht mit dem übergebenen identisch ist *schauder*
und dann auch noch ein lookup im fileheader *schauderschüttel*meine jetzige lösung ist ein
virtual edit *getedit() {return 0;} virtual button *getbutton() {return 0;} virtual window *getwindow() {return 0;} // ...
die jeweilige methode wird von den abgeleiteten klassen durch return this
ersetzt. das klappt ganz gut, ist dem prinzip ziemlich ähnlich aber ist schneller.was haltet ihr davon?
-
fragezeichen? schrieb:
meine jetzige lösung ist ein
virtual edit *getedit() {return 0;} virtual button *getbutton() {return 0;} virtual window *getwindow() {return 0;} // ...
die jeweilige methode wird von den abgeleiteten klassen durch return this
ersetzt. das klappt ganz gut, ist dem prinzip ziemlich ähnlich aber ist schneller.
was haltet ihr davon?ja, das war mein vorschlag. ist schon ganz ok. vielleicht überdenkenswert, statt 0 zurückzugeben, ne exception zu werfen. hängt davon ab, wie viele fehler du machen willst.
sämmtliche template-methoden verstecken den dynamic_cast nur.
wer sagt das? ich hab nicht verraten, wie die innendrin gehen. da gibts unzählige möglichkeiten. zum beispiel
template<typename T> T* getElement(int id) { Base* basep=privateGetGenericElement(id); if(basep->virtualGetClassEnum()!=T::staticGetClassEnum()) return throw 4711;//oder return 0 return static_cast<T*>(basep); }
-
Ich frage mich nur, woher du dann die ID's hast.. Wenn du die irgendwo speicherst, kannst du es ja machen, wie es ein Vorposter gesagt hat und gleich die Zeiger darauf speichern..
-
drakon schrieb:
Ich frage mich nur, woher du dann die ID's hast..
ich hab sie von "reinterpret_cast<button *>(g->getelement(1742)->getelement(8765))->settext(...);" im ersten posting.
im letzten posting scheint fragezeichen? sie nicht mehr zu brauchen.
-
volkard schrieb:
drakon schrieb:
Ich frage mich nur, woher du dann die ID's hast..
ich hab sie von "reinterpret_cast<button *>(g->getelement(1742)->getelement(8765))->settext(...);" im ersten posting.
im letzten posting scheint fragezeichen? sie nicht mehr zu brauchen.
Ah, ne Sorry. Ich habe auf den Post vor dir bezogen geschrieben.
Ich meinte im Programm allgemein. Wenn er die ID's irgendwo speichert, ala "myButtonIDContainer", dann ist das ganze Spiel ja eh für die Katze und man hätte gleich die Zeiger speichern können.
-
die IDs muss man sich halt merken. zeiger kann man sich aber nicht merken.
entweder ich speichere irgentwo unmengen an zeigern, die nur für eine methode
aufgerufen werden, oder ich löse das über IDs.(ich will in der rendermothode an einen knopf kommen der aus der main stammt)
die z.b. getedit methode braucht keine id, sie wird ja von einem generischen
zeiger aufgerufen, dessen typ zu ermitteln ist.bsp:
guimain->getactivepage()->getelement(1)->getelement(1)->getprogressbar()->setpercent(val);
getprogressbar gibt standardmäßig 0 zurück. nur wenn es sich wirklich um eine
progressbar handelt, kommt this zurück. das vermeidet gleichzeitig das casten.exeptions, hm.. naja.. das ist auch geschmackssache, aber es vergrößert den code
nur. außerdem fliegt die exeption eh wenn die methode 0 liefert.volkard schrieb:
wer sagt das? ich hab nicht verraten, wie die innendrin gehen.
weil
volkard schrieb:
ok, ich geb's zu: das ist nur ne andere schreibweise für
g->dynamic_cast<Window&>(getelement(1742)).dynamic_cast<Button&>(getelement(1765)).settext(...);
-
premature optimization schrieb:
Badestrand schrieb:
fragezeichen? schrieb:
dynamic_cast für (geringe) performance-probleme bekannt ist
Gegenüber dem, was dem cast dann folgt, ist das aber ein Witz
Also auf 64-Bit Windowssystemen können dynamic_casts ein Problem sein, vorallem wenn man ein paar 100000 pro Sekunde macht.
http://blogs.msdn.com/junfeng/archive/2006/10/17/dynamic-cast-is-slow-in-x64.aspx
http://archives.windowshpc.net/blogs/dev_gen/archive/2006/10/17/647.aspxWenn ich das richtig verstehe, ist das nicht Win64-, sondern MSVC64-spezifisch.
Falls dem so ist: hoffen wir mal, daß CodeGear es besser macht
-
dynamic_cast war bei MSVC immer schon langsam. Irgendwie krass dass MS es geschafft hat die noch langsamer zu machen. Und schade. Es gibt einige wenige Stellen wo dynamic_cast wirklich hilfreich wäre, und manchmal sind da auch performance-kritische Dinge dabei.
für MS
-
fragezeichen? schrieb:
volkard schrieb:
wer sagt das? ich hab nicht verraten, wie die innendrin gehen.
weil
volkard schrieb:
ok, ich geb's zu: das ist nur ne andere schreibweise für
g->dynamic_cast<Window&>(getelement(1742)).dynamic_cast<Button&>(getelement(1765)).settext(...);oh. ich hab nicht dazugeschrieben, daß ich innen schon sowas wie deine virtuellen funktionen haben wollte. ich mag nämlich dynamic_cast gar nicht.
anfangs dachte ich, daß dich die aufrufsyntax nervt und die unsicherheit, vielleicht nen nullzeiger zu benutzen.
"ok, ich geb's zu: das ist nur ne andere schreibweise für" war aus sicht des aufrufers gemeint.die template-funktion getElement merke ich mir, die wird praktisch sein, wenn ich mal groß bin und ein MMORPG schreibe. über's netz fliegen ja nur IDs von den mobs, vielleicht ist es dann gut, wenn ich alle objekte mit IDs verwalte. macht auch das speichern und laden viel einfacher.
-
fragezeichen? schrieb:
die IDs muss man sich halt merken. zeiger kann man sich aber nicht merken.
entweder ich speichere irgentwo unmengen an zeigern, die nur für eine methode
aufgerufen werden, oder ich löse das über IDs.Und das mit den IDs ist Quatsch wie du doch schon selber merkst. Ob du dir eine ID oder nen Pointer als Member einer Klasse hältst ist doch egal. Schau dir mal andere GUI Frameworks an.
http://doc.trolltech.com/4.0/examples.html
-
volkard schrieb:
die template-funktion getElement merke ich mir, die wird praktisch sein, wenn ich mal groß bin und ein MMORPG schreibe. über's netz fliegen ja nur IDs von den mobs, vielleicht ist es dann gut, wenn ich alle objekte mit IDs verwalte. macht auch das speichern und laden viel einfacher.
es lebe das id system
premature optimization schrieb:
Und das mit den IDs ist Quatsch wie du doch schon selber merkst. Ob du dir eine ID oder nen Pointer als Member einer Klasse hältst ist doch egal.
wenn man die ids eh als member speichert, macht das wenig sinn, da hast du recht.
wenn man aber statische ids vergibt, und in der lage ist sich diese 2min zu
merken, kann man in einer anderen methode oder funktion (die keinen zugriff
auf die verwaltungsklasse hat) den zeiger weiterverwenden.und wie volkard schon sagte, wenn man übers netz auf objekte zugreifen will,
werden zeiger sicherlich probleme machen. man kann die gültigkeit nicht
prüfen und das auf jedem client die daten an der selben stelle liegen, ist
sehr unwahrscheinlicht.
-
volkard schrieb:
die template-funktion getElement merke ich mir, die wird praktisch sein, wenn ich mal groß bin und ein MMORPG schreibe. über's netz fliegen ja nur IDs von den mobs, vielleicht ist es dann gut, wenn ich alle objekte mit IDs verwalte. macht auch das speichern und laden viel einfacher.
Ok, dass verstehe ich jetzt nicht. War das ironisch gemeint oder ist der Zugriff auf Objekte per ID wirklich so toll
Kann man sich denn die Objekte (Buttons, Scrollbars usw...) nicht in unterschiedlichen Listen merken und vieleicht mit sowas hier den Zeiger holen?
// Verschiedene Gui Elemente class Gui { public: enum type{ Undefined, Button, Scrollbar } } // ein Steuerelement class Button public Control { private: int type; public: Button() { type = Gui::Button; } } // ein Menü mit Buttons usw... class MainMenu public Gui { protected: std::map<int,Button*> buttons; std::map<int,Scrollbar*> bars; public: bool typeOf(id, Gui::type); Button* getButton(id); Scrollbar* getScrollbar(); void addElement( Control* ctrl, int posX, int posY ); } // irgendwo im Code int foo { MainMenu menu; menu.addElement( new Button(1234), x, y ); if( menu.typeOf(1234,Gui::Button) ) { Button* button = menu.getButton(id); button.setText("Ok"); } }
Ok der Code ist mit Vorsicht zu genießen (hab schon lange kein C++ mehr programmiert und jetzt auch keine Zeit die genaue Syntax rauszusuchen). Aber eigentlich könnte man doch auf diese Weise auch ohne dynamic Cast auskommen. Oder muss ich in addElement von Control* nach Button* casten?
-
fragezeichen? schrieb:
meine jetzige lösung ist ein
virtual edit *getedit() {return 0;} virtual button *getbutton() {return 0;} virtual window *getwindow() {return 0;} // ...
die jeweilige methode wird von den abgeleiteten klassen durch return this
ersetzt. das klappt ganz gut, ist dem prinzip ziemlich ähnlich aber ist schneller.was haltet ihr davon?
Es würde reichen, wenn du nur eine getFunktion in der Basisklasse definieren würdest.
Basis: virtual element* getTyp() = 0; Button: virtual button* getTyp() { return this; } Window: virtual window* getTyp() { return this; }
Nennt sich CoVariant-Returntype ...