Ich hatte die Aufgabe eine simple Hilfs-Klasse die dazu genutzt wird
Binärdaten aus einem Datenstrom zu verarbeiten zu devirtualisieren - die
Implementierung ist trivial aber durch die virtuals konnte der Kompiler
nicht inlinen - was auf Embedded Systemen dann doch gut messbare Zeiten
verursacht
1
classIBinaryReader
2
{
3
virtualvirtualuint16_tread_uint16()=0;
4
virtualvirtualuint16_tread_le_uint16()=0;
5
virtualvirtualuint16_tread_be_uint16()=0;
6
...32,64,...
7
};
1
classBinaryReader:publicIBinaryReader
2
{
3
// nur ein trivialer mem cast + swapping als Template-Methode
4
//(da auf dieser Platform Aligned Access nicht relevant ist)
5
}
alle Nutzungsstellen(knapp hundert) bekommen die IBinaryReader als
Referenz übergeben und es gibt nur die BinaryRead Klasse welches das
Interface implementiert, die beiden Klasse liegen in einem eindeutigen
Namespace
was ich gemacht habe:
1. alle Schnittstellen von IBinaryRead auf BinaryReader umgestellt
-Kompilierts: Ja
-Tests: OK
(git commit)
es gibt keinen Bezug mehr zu IBinaryReader ausser in der Ableitung
2. die IBinaryReader Ableitung entfernt d.h. BinaryReader hat jetzt
keine Basis-Klasse mehr
-Kompilierts: Ja
-Test: nicht OK?
wenn ich die Ableitung entferne fliegt mir irgendein Test um die Ohren
(sind eine Menge)
d.h. mein aktueller Stand ist ein #define welches steuert ob ich von dem
alten Interface ableite oder nicht - und mit Ableitung geht es und ohne
nicht
Bevor ich mich jetzt in die tiefen der nicht so sauber strukturierten
Tests stürze wollte ich mal eure Meinung hören
Ich denke es könnte damit zusammenhängen das durch die virtuellen das
inlining stärker beschränkt wird d.h. der Optimizer entfernt vielleicht
irgendetwas notwendiges was für ihn irrelevant erscheint
Tips/Ideen?
den Schritt 1 habe ich schon vor ein paar Wochen gemacht - das System
läuft seit dem sauber, alles Tests kommen durch, keine Klagen - ASAN,
Intel Inspector, Valgrind etc. sind still, in diesem Szenario gibt es
kein Multithreading
cppbert3 schrieb:> mh schrieb:>> Wie wärs mit ner Fehlermeldung?>> ist ein TEST_ASSERT das mir sagt das der Datenstrom beim> BinaryWrite/Read beschädigt wurde
und das passiert nur wenn ich die Interface-Ableitung entferne (die
natürlich keinen Code enthält)
cppbert3 schrieb:> mh schrieb:>> Wie wärs mit ner Fehlermeldung?>> ist ein TEST_ASSERT das mir sagt das der Datenstrom beim> BinaryWrite/Read beschädigt wurde
Dann solltest du aus diesem Test ein minimales und reproduzierbares
Beispiel ableiten und hier posten. Ich habe keine Ahnung wie man dir
sonst helfen sollte.
cppbert3 schrieb:> -Test: nicht OK?>> wenn ich die Ableitung entferne fliegt mir irgendein Test um die Ohren> (sind eine Menge)
Wie soll jemand hier basierend auf dieser Beschreibung erkennen, wo der
Fehler liegt?
cppbert3 schrieb:> Ich denke es könnte damit zusammenhängen das durch die virtuellen das> inlining stärker beschränkt wird d.h. der Optimizer entfernt vielleicht> irgendetwas notwendiges was für ihn irrelevant erscheint
Der Optimizer entfernt keine notwendigen Sachen. Wenn er etwas entfernt,
das du eigentlich brauchst, liegt das an einem Fehler in deinem
Programm. Viel mehr kann man dazu halt nicht sagen ohne mehr Info.
mh schrieb:> cppbert3 schrieb:>> mh schrieb:>>> Wie wärs mit ner Fehlermeldung?>>>> ist ein TEST_ASSERT das mir sagt das der Datenstrom beim>> BinaryWrite/Read beschädigt wurde>> Dann solltest du aus diesem Test ein minimales und reproduzierbares> Beispiel ableiten und hier posten. Ich habe keine Ahnung wie man dir> sonst helfen sollte.
Es geht mir mehr um Erfahrungswissen - bisher sind mir noch nie Tests um
die Ohren geflogen weil ich ein unnötiges(nicht genutztes) Interfaces
entfernt habe
die Stelle zu finden und zu korrigieren schaffe ich alleine - aber
vielleicht habt ihr Tips auf was ich besonders achten sollte und zwar
nur in Bezug auf die devirtualisierung
Rolf M. schrieb:> cppbert3 schrieb:>> -Test: nicht OK?>>>> wenn ich die Ableitung entferne fliegt mir irgendein Test um die Ohren>> (sind eine Menge)>> Wie soll jemand hier basierend auf dieser Beschreibung erkennen, wo der> Fehler liegt?
die Frage ist was das entfernen des Interfaces für Auswirkungen haben
kann - außer der der Optimizer dann viel stärker optimieren kann fällt
mir nichts ein und der Code ist so trivial das es in einem kleinen
Beispiel gar keinen Effekt hat, nur in den knapp 300.000 Zeilen in den
das gerade verwendet wird
> cppbert3 schrieb:>> Ich denke es könnte damit zusammenhängen das durch die virtuellen das>> inlining stärker beschränkt wird d.h. der Optimizer entfernt vielleicht>> irgendetwas notwendiges was für ihn irrelevant erscheint>> Der Optimizer entfernt keine notwendigen Sachen. Wenn er etwas entfernt,> das du eigentlich brauchst, liegt das an einem Fehler in deinem> Programm. Viel mehr kann man dazu halt nicht sagen ohne mehr Info.
natürlich liegt es an einem Fehler in dem Programm - das stelle ich
nicht in Frage - aber warum reicht das entfernen einer (nicht genutzen)
Ableitung schon aus?
cppbert3 schrieb:> Es geht mir mehr um Erfahrungswissen - bisher sind mir noch nie Tests um> die Ohren geflogen weil ich ein unnötiges(nicht genutztes) Interfaces> entfernt habe>> die Stelle zu finden und zu korrigieren schaffe ich alleine - aber> vielleicht habt ihr Tips auf was ich besonders achten sollte und zwar> nur in Bezug auf die devirtualisierung
Meine Erfahrung sagt: Du hast einen Fehler gemacht und suchst an der
falschen Stelle. Erstelle ein minimales Beispiel, das den fehler
reproduziert. In 95% der Fälle findest du dabei das Problem, das nichts
mit deiner ursprünglichen Annahme zu tun hat.
Wann habt ihr schon mal ein ungenutztes Interface entfernt und plötzlich
läuft der Code nicht mehr sauber durch - bei mir das 1. mal in 30 Jahren
- und die Software läuft Multiplatform, mit n Kompilern, wird permanent
mit ASAN, TSAN usw, getestet - ist jetzt also nicht gerade ein Moloch
aus Memory usw. Problemen
mh schrieb:> cppbert3 schrieb:>> Es geht mir mehr um Erfahrungswissen - bisher sind mir noch nie Tests um>> die Ohren geflogen weil ich ein unnötiges(nicht genutztes) Interfaces>> entfernt habe>>>> die Stelle zu finden und zu korrigieren schaffe ich alleine - aber>> vielleicht habt ihr Tips auf was ich besonders achten sollte und zwar>> nur in Bezug auf die devirtualisierung>> Meine Erfahrung sagt: Du hast einen Fehler gemacht und suchst an der> falschen Stelle. Erstelle ein minimales Beispiel, das den fehler> reproduziert. In 95% der Fälle findest du dabei das Problem, das nichts> mit deiner ursprünglichen Annahme zu tun hat.
die Unit-Tests für die beiden (Read/Write) Klassen laufen durch - mit
ohne ohne Ableitung, nur die Integrations-Tests meckern
Wie gesagt: ich werde genau das machen was ihr sagt (schon im ersten
Post geschrieben) - Was sollte man auch sonst machen? aber ich hoffte
auf Tips warum sich das entfernen einer Ableitung überhaupt auswirken
kann
cppbert3 schrieb:> alle Nutzungsstellen(knapp hundert) bekommen die IBinaryReader als> Referenz übergeben und es gibt nur die BinaryRead Klasse welches das> Interface implementiert, die beiden Klasse liegen in einem eindeutigen> Namespace
Wenn's den Aufwand lohnt... persönlich halt ich für die Klasse ein
Interface überhaupt zu haben, für ne schwachsinnige Designentscheidung.
Strategy-Pattern hin oder her, aber ganz sicher nicht für nen
BinärParser mit dieser Schnittstellenkopplung.
Btw. Inlining tut der doch nur, wenn du 'n __declspec(forceinline)
machst (MSVC), das default-Verhalten ansonsten ist doch eher ne
Empfehlung an den Compiler, dass er das inlinen kann, aber nicht muss.
Das ist beim GCC so, beim MSVC auch. Wenn der Compiler bei der
Kosten-Nutzen-Analyse auf die Idee kommt, dass inlining den Aufwand
nicht lohnt, wird er das auch nicht tun.
Aber Polymorphie/Interfaces, die nur von einer Klasse gebraucht wird,
ist halt eben auch Käse.
> 1. alle Schnittstellen von IBinaryRead auf BinaryReader umgestellt> 2. die IBinaryReader Ableitung entfernt d.h. BinaryReader hat jetzt> keine Basis-Klasse mehr
Im Prinzip ja, besser wär aber erstmal mit statischen casts zu arbeiten.
Also erstmal alles lassen wie's ist, Ableitung auch drinne lassen und
einfach die Tests mittels static_cast<BinaryReader&>(obj) umbauen, dass
sie ausschließlich von BinaryReader abhängig werden. Außer an genau der
einen Stelle des static_casts. Damit passte einen Test nach dem anderen
an, fertsch.
> wenn ich die Ableitung entferne fliegt mir irgendein Test um die Ohren> (sind eine Menge)
That's life, dafür sind Tests da.
> d.h. mein aktueller Stand ist ein #define welches steuert ob ich von dem> alten Interface ableite oder nicht - und mit Ableitung geht es und ohne> nicht
Das könnte ungünstig sein.
> Bevor ich mich jetzt in die tiefen der nicht so sauber strukturierten> Tests stürze wollte ich mal eure Meinung hören
In den Tests liegt die Wahrheit über deine Implementierung.
cppbert3 schrieb:> Was sollte man auch sonst machen? aber ich hoffte> auf Tips warum sich das entfernen einer Ableitung überhaupt auswirken> kann
Ach so: mach mal noch nen virtuellen Destruktor in deine
Interface-Klasse, sonst haste 'n potentielles Memleak.
db8fs schrieb:> Wenn's den Aufwand lohnt... persönlich halt ich für die Klasse ein> Interface überhaupt zu haben, für ne schwachsinnige Designentscheidung.> Strategy-Pattern hin oder her, aber ganz sicher nicht für nen> BinärParser mit dieser Schnittstellenkopplung.
genau das ist der Grund warum dieses sinnlose Interface entfernt werden
soll
ich verstehe auch nicht wofür hier ein Interface gebraucht wird
db8fs schrieb:> Btw. Inlining tut der doch nur, wenn du 'n __declspec(forceinline)> machst (MSVC), das default-Verhalten ansonsten ist doch eher ne> Empfehlung an den Compiler, dass er das inlinen kann, aber nicht muss.
durch die virtuelle darf er hier aber nicht mal inlinen wenn er es
möchte
db8fs schrieb:> Im Prinzip ja, besser wär aber erstmal mit statischen casts zu arbeiten.> Also erstmal alles lassen wie's ist, Ableitung auch drinne lassen und> einfach die Tests mittels static_cast<BinaryReader&>(obj) umbauen, dass> sie ausschließlich von BinaryReader abhängig werden.
den 1. Schritt habe ich schon vor Wochen gemacht - es gibt keinen Bezug
mehr zu dem Interface, und es wurden vorher nur BinaryReader Instanzen
uebergeben - alle Tests laufen durch, seit Wochen, keinerlei Probleme,
die Unit-Tests die Reader/Write testen haben 100% Abdeckung und laufen
unter ASAN und MSAN
db8fs schrieb:>> d.h. mein aktueller Stand ist ein #define welches steuert ob ich von dem>> alten Interface ableite oder nicht - und mit Ableitung geht es und ohne>> nicht>> Das könnte ungünstig sein.
nur ein ganz kleines bisschen :)
db8fs schrieb:>> Bevor ich mich jetzt in die tiefen der nicht so sauber strukturierten>> Tests stürze wollte ich mal eure Meinung hören>> In den Tests liegt die Wahrheit über deine Implementierung.
wenn ich wenigstens Code geschrieben hätte :/
cppbert3 schrieb:> 1. alle Schnittstellen von IBinaryRead auf BinaryReader umgestellt> -Kompilierts: Ja> -Tests: OK> es gibt keinen Bezug mehr zu IBinaryReader ausser in der Ableitung>> 2. die IBinaryReader Ableitung entfernt d.h. BinaryReader hat jetzt> keine Basis-Klasse mehr> -Kompilierts: Ja> -Test: nicht OK?
Also, von Schritt 1 auf 2 hast du tatsächlich ausschließlich
"BinaryReader : IBinaryReader" auf BinaryReader (ohne Ableitung)
geändert, ansonsten nichts?
Edit: Und der IBinaryReader hat keine Variablen, ist pure virtual und
der (virtual) Destruktor ist empty?
cppbert3 schrieb:> Ich denke es könnte damit zusammenhängen das durch die virtuellen das> inlining stärker beschränkt wird d.h. der Optimizer entfernt vielleicht> irgendetwas notwendiges was für ihn irrelevant erscheint
Klare Antwort: Nein.
Und ebenso klare Antwort: Eigentlich ist der Optimierer ziemlich gut
darin, zur Compilezeit auflösbare virtuelle Aufrufe durch direkte zu
ersetzen. Die ganze Übung könnte also eh umsonst gewesen sein.
Oliver
Oliver S. schrieb:> Und ebenso klare Antwort: Eigentlich ist der Optimierer ziemlich gut> darin, zur Compilezeit auflösbare virtuelle Aufrufe durch direkte zu> ersetzen. Die ganze Übung könnte also eh umsonst gewesen sein.>> Oliver
vorher waren die Methoden einer eigenen cpp/Lib drin - dann auch noch?
und eher geht es auch darum den trivial Code in eine Header-Only
variante zu überführen
btw: habe ich das Problem gefunden, es wurde mitten drin in meinem Umbau
eine Ableitung (nach alter Manier) von dem BinaryReader erstellt (mit
Verschluesselung) und durch die Ableitung(+den ersetzen
IBinaryReader-Parameter) wird natürlich so ein CryptBinaryReader jetzt
zu einem BinaryReader runtergestuft wenn dieser einfach per
BinaryReader& uebergeben wird - somit keine Verschlüsselung aka
ungleiche Daten
Wurde deswegen nicht in einem kleinen Test entdeckt weil die Tests
natürlich nicht davon ausgehen das ein Schlumpf die Ableitungs-Tradition
vernichten will
cppbert3 schrieb:> den 1. Schritt habe ich schon vor Wochen gemacht - es gibt keinen Bezug> mehr zu dem Interface, und es wurden vorher nur BinaryReader Instanzen> uebergeben - alle Tests laufen durch, seit Wochen, keinerlei Probleme,> die Unit-Tests die Reader/Write testen haben 100% Abdeckung und laufen> unter ASAN und MSAN
Klingt doch schon gut, dass die UTs funktionieren. Ach so, und weil die
Integrationstests nicht funktionieren, vermuteste irgendeine
Compilersache dahinter, die dir was zerbricht. Kannste ja mal die
Compilersettings für Integrationstests und UTs vergleichen.
Aber das mit der fehlenden virtuellen Destruktorimplementierung im
Interface sollteste auf jeden Fall untersuchen. Per default ist der
Destruktor des Interfaces nämlich sonst final, was ne schlechte Idee für
abgelittene Klassen ist.
cppbert3 schrieb:> Wurde deswegen nicht in einem kleinen Test entdeckt weil die Tests> natürlich nicht davon ausgehen das ein Schlumpf die Ableitungs-Tradition> vernichten will
Also haben die Voraussetzungen aus deinem ursprünglichen Post nicht
gestimmt? Gut das wir helfen konnten ...
cppbert3 schrieb:> btw: habe ich das Problem gefunden, es wurde mitten drin in meinem Umbau> eine Ableitung (nach alter Manier) von dem BinaryReader erstellt (mit> Verschluesselung) und durch die Ableitung(+den ersetzen> IBinaryReader-Parameter) wird natürlich so ein CryptBinaryReader jetzt> zu einem BinaryReader runtergestuft wenn dieser einfach per> BinaryReader& uebergeben wird - somit keine Verschlüsselung aka> ungleiche Daten
FCoI: Favor Composition over Inheritance.
Weils sonst die Frau Liskov mit der Substitionskeule zuschlagen kann in
tiefen Vererbungsketten...
db8fs schrieb:> FCoI: Favor Composition over Inheritance.
deswegen mag ich keine Vererbung - können komische Dinge passieren die
man sich das erstmal nicht erklären kann
mh schrieb:> Also haben die Voraussetzungen aus deinem ursprünglichen Post nicht> gestimmt? Gut das wir helfen konnten ...
ja gar nicht - ich bin nicht davon ausgegangen das nach klarer Vorgabe
noch jemand solche Ableitungen einbaut
cppbert3 schrieb:> db8fs schrieb:>> FCoI: Favor Composition over Inheritance.>> deswegen mag ich keine Vererbung - können komische Dinge passieren die> man sich das erstmal nicht erklären kann
Vererbung ist ein Sprachmittel, was halt viele implizite Annahmen
trifft. Zur Laufzeit liegt ja das Objekt der Basisklasse eigentlich
auch wie bei ner Komposition im Objekt der Ableitung drinne, wenn man
mitm Debugger drauf guckt.
Abgesehen davon refaktorisieren sich tiefe Ableitungen ziemlich
bescheiden aus Legacy-Code wieder raus, weswegen man sich echt vor dem
fröhlichen Ableiten überlegen sollte, ob man das einbaut oder nicht.
Wenns Code zum wegschmeißen ist, isses wahrscheinlich egal.
cppbert3 schrieb:> und durch die Ableitung(+den ersetzen> IBinaryReader-Parameter) wird natürlich so ein CryptBinaryReader jetzt> zu einem BinaryReader runtergestuft wenn dieser einfach per> BinaryReader& uebergeben wird - somit keine Verschlüsselung aka> ungleiche Daten
Dafür hat man in C++ das Schlüsselwort override eingefügt, das man bei
allen virtuellen Memberfunktionen, die in abgeleiteten Klassen
überschrieben werden, dazuschreiben sollte. Dann hätte es nach der
Änderung einen Fehler gegeben, weil keine virtuelle Memberfunktion mehr
überschrieben wird.
Bei gcc kann man sich mit -Wsuggest-override eine Warnung ausgeben
lassen, wenn eine virtuelle Funktion ohne override-Schlüsselwort
überschrieben wird, um damit zu erzwingen, dass override dort genutzt
wird, wo es sinnvoll ist.
Und man kann Klassen als "final" definieren, womit man verbietet, dass
davon abgeleitet wird.
Oliver S. schrieb:> Und ebenso klare Antwort: Eigentlich ist der Optimierer ziemlich gut> darin, zur Compilezeit auflösbare virtuelle Aufrufe durch direkte zu> ersetzen.
Wie soll er das in einer Funktion machen, die als Parameter eine
Referenz auf die Klasse bekommt? Dort weiß der Compiler nicht, ob die
Funktion von irgendwo aufgerufen wird, wo eine Instanz einer davon
abgeleiteten Klasse übergeben wird. Er kann das höchstens, wenn er alle
Aufrufe kennt (also die Funktion static ist) und er für alle Aufrufe
weiß, dass das Objekt nicht von einer abgeleiteten Klasse sein kann.
Rolf M. schrieb:> Dafür hat man in C++ das Schlüsselwort override eingefügt, das man bei> allen virtuellen Memberfunktionen, die in abgeleiteten Klassen> überschrieben werden, dazuschreiben sollte. Dann hätte es nach der> Änderung einen Fehler gegeben, weil keine virtuelle Memberfunktion mehr> überschrieben wird.> Bei gcc kann man sich mit -Wsuggest-override eine Warnung ausgeben> lassen, wenn eine virtuelle Funktion ohne override-Schlüsselwort> überschrieben wird, um damit zu erzwingen, dass override dort genutzt> wird, wo es sinnvoll ist.> Und man kann Klassen als "final" definieren, womit man verbietet, dass> davon abgeleitet wird.
ich kann hier nur C++03
Rolf M. schrieb:> Oliver S. schrieb:>> Und ebenso klare Antwort: Eigentlich ist der Optimierer ziemlich gut>> darin, zur Compilezeit auflösbare virtuelle Aufrufe durch direkte zu>> ersetzen.
wenn ich nur die virtuelle Basis-Klasse wieder einführe werde ich laut
VTune fast 200ms langsamer (in einem 10sek run) - was schon relevant für
mich ist
cppbert3 schrieb:> ich kann hier nur C++03
Wenn der Compiler so alt ist, könnte das daran liegen. Da hat sich in
den letzten Jahren schon einiges an den Optimierungen getan.
Oliver
Oliver S. schrieb:> cppbert3 schrieb:>> ich kann hier nur C++03>> Wenn der Compiler so alt ist, könnte das daran liegen. Da hat sich in> den letzten Jahren schon einiges an den Optimierungen getan.>> Oliver
ich darf nur C++03, mein Testkompiler ist aber x64 mit aktuellem VS2019
wenn der User-Code kein Template/Header-only ist frage ich mich trotzdem
wie da der Kompiler die virtuellen weg optimieren soll - er weiß doch
gar nicht welche Implementierung ich ihm möglicherweise mal vorwerfen
werden - wenn ich die Lib linke
Oliver S. schrieb:> Wenn du in der lib die abgeleiteten Klassen benutzt, müssen die da> komplett definiert sein. Da kannst du der nichts unbekanntes mehr> vorwerfen.
aber nur wenn die keine virtuelle Basis hat - weil dann könnte ich immer
noch Ableitungen von der Ableitung übergeben, wenn irgendwas virtuell
ist darf der Optimizer nicht einfach nur den Parameter-Typ
implementieren - das reicht nicht, und deswegen sind auch die meisten
virtuellen eben nicht weg-optimierbar
in meinem Fall fallen die virtuelle aber eh ganz weg, d.h. der Optimizer
hat gar keine virtuellen zum weg-optimieren - aber du bezogst dich in
deiner Aussage darauf das es wohl gar nicht nötig war weil der Kompiler
das eh optimiert - was aber in diesem Kontext gar nicht gehen kann
cppbert3 schrieb:> in meinem Fall fallen die virtuelle aber eh ganz weg, d.h. der Optimizer> hat gar keine virtuellen zum weg-optimieren - aber du bezogst dich in> deiner Aussage darauf das es wohl gar nicht nötig war weil der Kompiler> das eh optimiert - was aber in diesem Kontext gar nicht gehen kann
Vielleicht erkennt er aber in den nun nicht-virtuellen Methoden Dinge,
die er wegoptimieren kann.
Welche Warnungen gab es denn vor und nach der Änderung?
Solange es diese nämlich gibt, hat der Compiler u.U. sich die Freiheit
genommen, Mehrdeutiges in genau einem Sinne zu interpretieren, der nicht
mit der Intension des Autors übereinstimmen muß.
Carl D. schrieb:> cppbert3 schrieb:>> in meinem Fall fallen die virtuelle aber eh ganz weg, d.h. der Optimizer>> hat gar keine virtuellen zum weg-optimieren - aber du bezogst dich in>> deiner Aussage darauf das es wohl gar nicht nötig war weil der Kompiler>> das eh optimiert - was aber in diesem Kontext gar nicht gehen kann>> Vielleicht erkennt er aber in den nun nicht-virtuellen Methoden Dinge,> die er wegoptimieren kann.>> Welche Warnungen gab es denn vor und nach der Änderung?> Solange es diese nämlich gibt, hat der Compiler u.U. sich die Freiheit> genommen, Mehrdeutiges in genau einem Sinne zu interpretieren, der nicht> mit der Intension des Autors übereinstimmen muß.
das Problem ist schon gelöst:
Beitrag "Re: C++, virtual, inline, Fehler wenn ich abstrakte Basis-Klasse entferne"
Ich wollte nur noch mal verstehen wie Oliver darauf kommt das die
virtuellen im Original weg optimiert werden könnten, egal ob der
Kompiler alt oder aktueller ist
cppbert3 schrieb:> Ich wollte nur noch mal verstehen wie Oliver darauf kommt das die> virtuellen im Original weg optimiert werden könnten, egal ob der> Kompiler alt oder aktueller ist
Beim 2005/2008er Studio gabs ne Option __declspec(novtable), die man für
das Interface machen konnte, gibt vielleicht auch beim GCC irgendein
Attribut dafür, keine Ahnung. Dann verhält sich der Compiler so, dass er
das virtual ignoriert und macht, glaube ich, auch die Datenstrukturen
dann selber kleiner zur Laufzeit. Hatte ich vor gefühlten Ewigkeiten
(5/6 Jahre) mal verwenden müssen, weiß aber nicht mehr, in welchem
Kontext.
Per default konnte man das aber beim MSVC glaub ich nicht per
Compilerswitch einschalten, aber so genau weiß ichs jetzt auch nicht
mehr und googlen möcht ich jetzt nicht.
Der Sinn erschließt sich mir nicht so ganz. Warum macht man die
Funktionen virtual und stellt dann über ein zusätzliches Attribut den
virtual-Charakter wieder ab?
Rolf M. schrieb:> Der Sinn erschließt sich mir nicht so ganz. Warum macht man die> Funktionen virtual und stellt dann über ein zusätzliches Attribut den> virtual-Charakter wieder ab?
Na ja, soweit ich's weiß, war das hauptsächlich für Interfaceklassen mit
rein virtuellen Methoden gedacht, die mit =0 angelegt worden sind.
Bei dem Windows COM-Geraffel gabs wohl vermutlich häufiger ein riesiges
Pamphlet an Interfaces, die wohl per IDL auch andere Interfacetypen in
den Methodensignaturen stehen haben konnten bzw. sogar ganze
Interface-Ableitungshierarchien dahinter, virtuelle Vererbung mal noch
ganz außen vor.
Um da die Objekte zur Laufzeit nicht riesig groß durch die vtables
werden zu lassen, konnte man glaub ich, dieses declspec da nutzen. Ich
hatte das glaube ich, damals auf WinCE mal genutzt, ich glaube aus dem
Grund, mal selber zu viele Interfaceklassen da reingebastelt gehabt zu
haben, die dann aus Platzgründen wieder kompakter werden mussten.
Irgendsowas in der Art.
db8fs schrieb:> Rolf M. schrieb:>> Der Sinn erschließt sich mir nicht so ganz. Warum macht man die>> Funktionen virtual und stellt dann über ein zusätzliches Attribut den>> virtual-Charakter wieder ab?>> Na ja, soweit ich's weiß, war das hauptsächlich für Interfaceklassen mit> rein virtuellen Methoden gedacht, die mit =0 angelegt worden sind.
Dann kann aber wirklich nur die vtable wegoptimiert werden, nicht der
vptr?
mh schrieb:> Dann kann aber wirklich nur die vtable wegoptimiert werden, nicht der> vptr?
Ich glaub, das Teil kann auch für den vfptr einer einzelnen Methode
genommen werden, quasi wie ein __declspec(dllexport), also nicht nur für
die vtable, die die Polymorphie beschreibt.
Wenn aber eine mit novtable gedeclspecte Basisklasse aber Zustand hat
(Membervariablen) und man greift darauf zu (bzw. ruft eine
implementierte, aber gedeclspecte Basisklassenmethode auf), qualmt es
dann halt. Muss man halt entweder wissen oder merkt man dann schon. ^^
Praktisch isses irgendwo, gerade für rein-virtuelle (bis halt auf'n
Destruktor) Interfaceklassen.
Für sowas wär halt für C++ wie bei Java/C#/D so'n Keyword 'interface'
nützlich, dass den Destruktor virtuell macht + automatisch
implementiert. Und vielleicht ne das __declspec(novtable) per default
dafür macht. Es wird oft empfohlen, große Vererbungshierarchien in C++
zu vermeiden, warum also nicht per switch sowas de/aktivierbar machen.
db8fs schrieb:> Ich glaub, das Teil kann auch für den vfptr einer einzelnen Methode> genommen werden, quasi wie ein __declspec(dllexport), also nicht nur für> die vtable, die die Polymorphie beschreibt.
Was genau meinst du damit? Soll damit der Pointer für diese eine
Funktion aus der vtable genommen werden? Das ergibt alles irgendwie
wenig Sinn.
Gerade nachgeguckt, es macht etwas ganz anderes, als du glaubst.
https://docs.microsoft.com/en-us/cpp/cpp/novtable?view=msvc-160