Forum: PC-Programmierung C++, virtual, inline, Fehler wenn ich abstrakte Basis-Klasse entferne


von cppbert3 (Gast)


Lesenswert?

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
class IBinaryReader
2
{
3
  virtual virtual uint16_t read_uint16() = 0;
4
  virtual virtual uint16_t read_le_uint16() = 0;
5
  virtual virtual uint16_t read_be_uint16() = 0;
6
  ... 32,64,...
7
};
1
class BinaryReader: public IBinaryReader
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?

von cppbert3 (Gast)


Lesenswert?

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

von mh (Gast)


Lesenswert?

Wie wärs mit ner Fehlermeldung?

von cppbert3 (Gast)


Lesenswert?

mh schrieb:
> Wie wärs mit ner Fehlermeldung?

ist ein TEST_ASSERT das mir sagt das der Datenstrom beim 
BinaryWrite/Read beschädigt wurde

von cppbert3 (Gast)


Lesenswert?

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)

von mh (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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?

von mh (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von db8fs (Gast)


Lesenswert?

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.

von db8fs (Gast)


Lesenswert?

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.

von cppbert3 (Gast)


Lesenswert?

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 :/

von Mikro 7. (mikro77)


Lesenswert?

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?

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von db8fs (Gast)


Lesenswert?

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.

von Vincent H. (vinci)


Lesenswert?

Hat das irgendeinen Grund dass ihr das "virtual" Keyword verdoppelt?

von mh (Gast)


Lesenswert?

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 ...

von db8fs (Gast)


Lesenswert?

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...

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von db8fs (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von cppbert3 (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

Wenn du in der lib die abgeleiteten Klassen benutzt, müssen die da 
komplett definiert sein. Da kannst du der nichts unbekanntes mehr 
vorwerfen.

Oliver

von cppbert3 (Gast)


Lesenswert?

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

von Carl D. (jcw2)


Lesenswert?

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ß.

von cppbert3 (Gast)


Lesenswert?

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

von db8fs (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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?

von db8fs (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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?

von db8fs (Gast)


Lesenswert?

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.

von mh (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.