C++ typeof Äquivalent / switch nach Objekttyp
-
Stimmt.
-
SeppJ schrieb:
Wie wäre es, wenn du der Basisklasse einen Methodenzeiger spendierst, der dann bei der Konstruktion einer abgeleiteten Klasse passend gesetzt wird? Dann bräuchten überhaupt keine Abfragen irgendeiner Art gemacht werden für den kleinen Preis von ein paar Byte Speicher.
Das hört sich fantastisch an! Aber ehrlich gesagt, komme ich da an meine Grenzen. Habe keine Ahnung, wie das programmiert werden kann. Könntest Du vielleicht ein lauffähiges Minimal-Beispiel zeigen?
-
sebi707 schrieb:
roflo schrieb:
SeppJ schrieb:
Wie wäre es, wenn du der Basisklasse einen Methodenzeiger spendierst, der dann bei der Konstruktion einer abgeleiteten Klasse passend gesetzt wird? Dann bräuchten überhaupt keine Abfragen irgendeiner Art gemacht werden für den kleinen Preis von ein paar Byte Speicher.
= virtuelle Methode
Fast. Ist eine Indirektion weniger.
Welche aber nichts kosten würde unter der Annahme, daß die Speicherseiten mit den VTBLs heiß bleiben und es einen Call-Befehl gäbe, der den Tabellen-Nachkucker gleich in einem Aufwasch mitmachen könnte.
-
mireiner schrieb:
SeppJ schrieb:
Wie wäre es, wenn du der Basisklasse einen Methodenzeiger spendierst, der dann bei der Konstruktion einer abgeleiteten Klasse passend gesetzt wird? Dann bräuchten überhaupt keine Abfragen irgendeiner Art gemacht werden für den kleinen Preis von ein paar Byte Speicher.
Das hört sich fantastisch an! Aber ehrlich gesagt, komme ich da an meine Grenzen. Habe keine Ahnung, wie das programmiert werden kann. Könntest Du vielleicht ein lauffähiges Minimal-Beispiel zeigen?
Ja, das Minimalbeispiel ist ganz einfach: virtual. Das ist ja gerade der ganze Witz. Du brichst dir hier einen ab für seit 20 Jahren veraltete Optimierungen.
Hör auf volkard!
volkard schrieb:
Das Ruckeln liegt nicht daran.
Da hat volkard sicher recht. Ich bezweifle auch sehr deine Messmethode, wenn du behauptest, 4% gegenüber der Implementierung von virtuellen Methoden deines Compilers gewonnen zu haben. Das schaffst du ganz sicher nicht mit typeid/typeof & Co. Denn was glaubst du wohl, wie typeid und Konsorten implementiert sind?
Zeig lieber dein eigentliches Problem, also das Ruckeln, und frag, wie man das flüssig hin bekommt. Wobei das Problem eventuell im Spiele- und Grafikforum besser aufgehoben sein könnte.
-
@mireiner
Damit das ganze ne Chance hat 'was zu bringen darf der Aufruf nicht indirekt erfolgen - die Methode muss "direkt" aufgerufen werden. Damit sie inline erweitert werden kann. Denn nur dort sind die wirklich mächtigen Optimierungen zu Hause.Das kannst du erreichen durch
* Einen mächtig schlauen Compiler der mächtig schlau Devirtualization betreiben kann, und ausreichend Glück dass er es an der Stelle wo du es brauchst auch macht.
* Einen mächtig schlauen Compiler den du mit Profile-Guided-Optimization unterstützt + immer noch ein gutes Stück Glück.
* Dieswitch-case
Variante.Ein Methodenzeiger bringt nichts, da hier der Aufruf erst wieder indirekt erfolgt, also kein Inlining möglich ist (zumindest praktisch nicht, auch wenn auch hier ein Compiler theoretisch optimieren könnte).
Auf den mächtig schlauen Compiler würde ich mich nicht verlassen wollen.
Bleibt also noch die
switch-case
Variante.Dabei ist natürlich wichtig dass du an das auf was du
switch
-st möglichst schnell dran kommst. Lösung: pack nenint
in die Basisklasse.switch (m_myType) { case Base::MyType: Base::foo(); break; case Sub1::MyType: static_cast<Sub1*>(this)->Sub1::foo(); break; case Sub2::MyType: static_cast<Sub2*>(this)->Sub2::foo(); break; default: this->foo(); break; }
Bloss ... dummerweise wird volkard vermutlich Recht haben. Das Ruckeln wird wohl nicht dort verursacht werden. Und dann kannst du es dort natürlich auch nicht beheben.
-
sebi707 schrieb:
wenn ich das richtig sehe dann ist das ein Vorschlag für eine Optimierung in den Compilern, nichts zum selbst programmieren bei jedem Funktionsaufruf. Dazu ist der Artikel von 1996. Falls diese Optimierung sinnvoll ist/war haben heutige Compiler das hoffentlich eingebaut.
Ja. Ist drin, sollte alle "grossen" aktuellen Compiler können (MSVC, GCC, Clang, Intel)
Nennt sich Devirtualization.
Ist aber natürlich, wie viele Optimierungen, nix worauf man sich 100% verlassen kann. Wenns um die Wurst geht darf man sowas in 1-2 Funktionen ruhig mal selbst machen. Geht bloss meist nicht um die Wurst
-
Das erinnert mich an das oft vergessene C++11-Feature final, mit dem man dem optimierenden Compiler die Annahmen garantieren kann (die dann hoffentlich auch stimmen!), die er braucht, um diese Optimierung unbekümmert durchführen zu können.
-
Bringt
final
da wirklich was?Der üblicher Fall ist doch dass die Stelle wo der kritische Aufruf steht eben gerade nicht in der
final
Klasse ist (bzw. nicht in der Klasse wo die aufgerufene Funktionfinal
wird).Und dann bringt es dem Compiler schätze ich nicht viel zu wissen dass bestimmte abgeleitete Klassen
final
wären.Er muss also trotzdem ein
if (this->vtbl->fun[123] == &Derived1::Fun)
o.Ä. reinknallen. Und das reicht auch ohnefinal
.Die schwierigen Fragen für den Compiler sind denke ich eher: wo zahl sich das überhaupt aus und welche abgeleiteten Klassen sollten als Kandidaten geprüft werden?
-
Wenn du den Fall, in dem final in diesem Zusammenhang etwas bringen würde, als unrealistisch weg definierst (womit du aber durchaus Recht haben magst), dann bringt es natürlich nichts
-
Also, final bringt nur dann was, wenn der Compiler den most derived type nicht kennt, aber trotzdem noch Zusatzinformationen hat.
Also z.B. hier:
Base* base=createObject(); static_cast<Derived*>(base)->foo();
Ohne final in Derived::foo() resultiert das trotzdem in einen virtuellen Funktionsaufruf, mit final kann Derived::foo() direkt aufgerufen werden.
Wenn man
static_cast<Derived*>(base)->Derived::foo()
schreibt, ist's eh egal.
-
;qwerty schrieb:
Wenn man
static_cast<Derived*>(base)->Derived::foo()
schreibt, ist's eh egal.Ich denke typisch ist eher der Fall
base->foo()
.
Und da bringtfinal
wohl auch wenig.
-
hustbaer schrieb:
Bringt
final
da wirklich was?Sobald der Compiler alle Blätter kennt, kann er switch machen.
Das sehe ich jetzt eigentlich nur, wenn man eine Library als DLL zur Verfügung stellt. Ohne final kann man in der DLL keine Blätter haben (in der EXE schon).
-
volkard, ich hab keine Ahnung was du meinst.
Wieso sollte es wichtig sein zu wissen was Blätter sind und was nicht?
Und wieso sollte man das wissen müssen damit der Compiler "switch machen" kann? Der Compiler kann immer "switch machen", er muss nur im Normalfall einendefault
Zweig einbauen der den Aufruf ganz normal über den Vtable macht.
-
hustbaer schrieb:
er muss nur im Normalfall einen
default
Zweig einbauen der den Aufruf ganz normal über den Vtable macht.stimmt. dann sehe ich erstmal keinen vorteil in final.