[C++] Exceptions und Module

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[C++] Exceptions und Module

Beitrag von Krishty »

Hi,

Sollte DLL-Code Exceptions schmeißen? Man erzählt gern, solange die Exception in allen Modulen gleich deklariert und kompiliert wird, sei das okay – aber es gibt da noch andere Fallstricke … wie regelt ihr das? Greift ihr für Plugins ausschließlich auf die guten alten Return-Codes zurück? Gibt es sichere Wege, nicht-native Typen als Exceptions über DLL-Grenzen hinweg zu werfen?

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Tactive
Beiträge: 61
Registriert: 21.07.2004, 15:10
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Tactive »

Hallo,

solange die DLL sich im selben Addressraum befindet, wie die eigendliche Anwendung ist es in Ordnung wenn sie eine Exception wirft. Ich verwende die alten Return-Codes jedenfalls nur noch sehr selten, da es einfacher ist eine Exception zu fangen als den Quellcode mit unnötigen if-Abfragen aufzublähen. Zudem kann die Exception auch auf höhere Ebene gefangen werden.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Tactive hat geschrieben:solange die DLL sich im selben Addressraum befindet, wie die eigendliche Anwendung ist es in Ordnung wenn sie eine Exception wirft. […] Zudem kann die Exception auch auf höhere Ebene gefangen werden.
Genau das ist das Problem. Stell dir vor, eine Exception in einer DLL sorgt für einen vollständigen Abbruch des Programms. Während sie immer weiter nach oben dirigiert wird, sorgt RAII idealerweise dafür, dass alle Ressourcen des Programms freigegeben werden – darunter fallen aber auch die DLLs. Besitzt die Exception-Klasse virtuelle Funktionen (wie alles, was von ::std::exception erbt), verweist ihr vftable irgendwann auf den Adressraum der nun freigegebenen DLL und beim Abfangen fliegt einem alles um die Ohren.

Man muss in diesem Fall also einen Mechanismus einbauen, der jede Exception, die aus der DLL kommt, kopiert (damit ein Objekt mit vftable im Adressraum des aufrufenden Moduls entsteht) – dafür muss man aber jeden Exception-Typ, der von der DLL geworfen werden kann, kennen, identifizieren und von Hand eine Kopie erzwingen (throw; tut das nämlich leider nicht). Dort fängt die Sache dann an, hässlicher zu werden als Return-Codes … :/
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

Ist statisches Linken evtl. eine Alternative? Damit würdest all diese Probleme umgehen. Ansonsten wird's knifflig - entweder alles als Kopie werfen und nicht mit new erzeugen, oder Exceptions am DLL-Rand fangen und den dazugehörigen Stub-Funktionen neu werfen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Schrompf hat geschrieben:Ist statisches Linken evtl. eine Alternative? Damit würdest all diese Probleme umgehen.
Momentan könnte ich noch statisch linken, mit fortschreitender Entwicklung aber nicht mehr, also nein.
Schrompf hat geschrieben:alles als Kopie werfen und nicht mit new erzeugen
Wie genau meinst du das?

Ich habe schon versucht, die Exceptions by-value zu fangen statt by-reference, in der Hoffnung, dass der Copy-C’tor die virtuellen Funktionen nicht anrührt. Dann explodieren aber die Interna der CRT mit irgendwelchem Mutex-Locks in ihren Frame-Handlern, und da der D’tor ebenfalls virtuell ist, würde es später sowieso krachen :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von kimmi »

Ich habe mal im C++ Coding Standard hierzu nachgelesen. Die empfehlen, keine Exceptions über Modulgrenzen zu werfen ( genau, um die von dir beschriebenen Probleme zu vermeiden ). Dementsprechend würde ich wie Schrompf dazu raten, an den Modulgrenzen die Exception zu fangen und entweder entsprechend den Fehler protokollieren bzw. im neuen Modul eine neue Exception zu werfen.
Also Dll-Exceptions benutzen? -> Ja, aber auch nur in dem Modul / Dll und nicht darüber hinaus. Die Exception kannst du ja z.B. in der DllMain ( wenn du Windows benutzt ) den Fehlerfall abfangen und darauf reagieren.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Okay, da man ja generell keine STL-Objekte über Modulgrenzen austauschen soll, werde ich eine eigene, nicht von ::std::exception abgeleitete Klasse definieren, um Ausnahmen unter den Modulen auszutauschen. Die fange ich hinter den Modulgrenzen ab und konvertiere sie zu üblichen Exceptions. Die Module müssen dann eben sicherstellen, dass sie keine anderen Ausnahmen werfen.

Die externen Ausnahmen sollen außerdem noch die Information enthalten, aus welchem Modul sie ursprünglichen stammen. Das erschwert mir momentan die Design-Entscheidungen, aber ich denke, ich kriege das ab hier allein hin. Danke für die Hilfe :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

Krishty hat geschrieben:
Schrompf hat geschrieben:Ist statisches Linken evtl. eine Alternative? Damit würdest all diese Probleme umgehen.
Momentan könnte ich noch statisch linken, mit fortschreitender Entwicklung aber nicht mehr, also nein.
Darf ich fragen, was die weitere Entwicklung damit zu tun hat? Ich rieche hier nämlich wieder nur einen Fall von "Engine muss als DLL daherkommen, weil das halt alle machen". Dafür gibt es aber keinen Grund, der die Arbeit wert wäre. Es scheint einfach nur die "Coolness" zu sein, weswegen alle Leute ihre heimische Engine in DLLs sehen wollen.
Schrompf hat geschrieben:alles als Kopie werfen und nicht mit new erzeugen
Wie genau meinst du das?
Klingt, als hättest Du das probiert. Ich meinte damit folgendes - normalerweise

Code: Alles auswählen

throw new MyExceptionClass( "Text");

...

try {
...
} catch( MyExceptionClass* ex)
{
  .. tu was..
  delete ex;
}
Und ich hätte jetzt probiert, stattdessen den Kopierkonstruktor zu strapazieren:

Code: Alles auswählen

throw MyExceptionClass( "text");

...

try {
  ...
} catch( MyExceptionClass ex)
{
  .. tu was ..
}
Also ohne new, und damit kopiert bei jeder Übergabe. Allerdings darf die Exception auch keine dynamisch allokierten Typen enthalten, weil Du dann wieder in das klassische DLL-Heap-Problem rennst. Wäre das einen Versuch wert?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Schrompf hat geschrieben:Darf ich fragen, was die weitere Entwicklung damit zu tun hat? Ich rieche hier nämlich wieder nur einen Fall von "Engine muss als DLL daherkommen, weil das halt alle machen". Dafür gibt es aber keinen Grund, der die Arbeit wert wäre. Es scheint einfach nur die "Coolness" zu sein, weswegen alle Leute ihre heimische Engine in DLLs sehen wollen.
Fragst du mich tatsächlich gerade, was der Vorteil von Modularisierung ist? Mal ganz praktisch gesehen:

1. Kompilierzeiten: Ändere ich Grundlagen in einer Komponente, braucht die Engine Minuten zum neukompilieren, und dabei habe ich gerade mal die Basis implementiert … mit DLLs reduziert sich das. Insbesondere, wenn ich in der Uni am Netbook arbeite, wo kompilieren nicht nur fünfmal so lange dauert wie am Desktop, sondern auch auf die Akkulaufzeit geht, ist das unschätzbar.

2. Dritt-APIs: Leider hat z.B. D3D die Angewohnheit, das Laden seiner Run-Time selbst zu übernehmen bevor auch nur ein Fetzen meines Codes ausgeführt wird. Im Klartext heißt das: Wenn ich in der weiteren Entwicklung zu D3D linke, wird das statische Programm auf Systemen, auf denen kein D3D11 installiert ist, mit Verweis auf die fehlende d3d11.dll abstürzen und ich kann nichts dagegen tun, egal, ob ich auf meinem Drittrechner zum schnellen Debuggen zwischendurch ohne Renderer auskommen kann oder nicht.

3. Ordentliche Versionsverwaltung, schneller Verzicht auf Komponenten zwecks einfacherem Debugging, später mal Erweiterbarkeit, sollte das Projekt tatsächlich mal fertig werden (alte Laier ab hier).

Wenn das Projekt fertig ist, werde ich sicher auch mal statische Versionen raushauen … aber für die Entwicklungszeit kann ich ohne DLLs nicht leben.
Also ohne new, und damit kopiert bei jeder Übergabe. Allerdings darf die Exception auch keine dynamisch allokierten Typen enthalten, weil Du dann wieder in das klassische DLL-Heap-Problem rennst. Wäre das einen Versuch wert?
Ich sehe hier zum ersten Mal, dass Exceptions auf dem Heap allokiert und beim Abfangen freigegeben werden … kannst du mir sagen, warum man das macht (nicht offensiv gemeint, ernste Frage)?

Bisher habe ich Exceptions immer by-value geworfen und by-reference gefangen:

Code: Alles auswählen

try {
    throw ::std::runtime_error("foo");
} catch(::std::runtime_error const & e_) {
    …
}
Aber auch das Fangen by-value klappt nicht. Was DLL-Heaps angeht, ist das neben dem vftable-Problem schon der zweite Grund, Exceptions über DLL-Grenzen sein zu lassen Mir bleibt offenbar wirklich nichts, als immer direkt an der Schnittstelle zu hocken und Exceptions direkt abzufangen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

[Hastiges Edit: sorry, ich hab grad Deinen Beitrag editiert, anstatt meine Antwort in einen neuen Beitrag zu schreiben. Ich hab jetzt hoffentlich alles wieder hergestellt, wie es vorher war]
Krishty hat geschrieben:
Schrompf hat geschrieben:Darf ich fragen, was die weitere Entwicklung damit zu tun hat? Ich rieche hier nämlich wieder nur einen Fall von "Engine muss als DLL daherkommen, weil das halt alle machen". Dafür gibt es aber keinen Grund, der die Arbeit wert wäre. Es scheint einfach nur die "Coolness" zu sein, weswegen alle Leute ihre heimische Engine in DLLs sehen wollen.
Fragst du mich tatsächlich gerade, was der Vorteil von Modularisierung ist?
Nein, ich habe Dich gefragt, warum Du eine selbstgeschriebene und von Dir sowieso selbst kompilierte Lib dynamisch anstatt statisch linkst. Fremdcode musst Du nehmen, wie er daherkommt - als DLL oder nicht. Daher ist Dein D3D11-Beispiel schonmal hinfällig. Kompiliert wird sowieso nur, was sich geändert hat - also Deine Lib, egal ob Du statisch oder dynamisch linkst. Die Versionsverwaltung schiebt nur geänderte Dateien hin und her - egal ob Du statisch oder dynamisch linkst. Und wenn Du was an einem Header Deiner Engine änderst, muss sowieso auch das abhängige Hauptprogramm neu kompiliert werden - egal, ob Du statisch oder dynamisch linkst.

Ich verstehe daher auch weiterhin nicht, warum es unbedingt eine DLL sein muss, wenn eine LIB exakt die gleichen Vorteile bietet.
Krishty hat geschrieben:Ich sehe hier zum ersten Mal, dass Exceptions auf dem Heap allokiert und beim Abfangen freigegeben werden … kannst du mir sagen, warum man das macht (nicht offensiv gemeint, ernste Frage)?
Weil ich keinen Überblick hatte, wie die Lebenszeit bzw. der Scope des Exception-Objekts ist, was ich da werfe. Wann es konstruiert wird, ob es kopiert wird, wann es zerstört wird. Daher wollte ich auf Nummer sicher gehen.

So, wie ich das jetzt verstehe, bringt das geworfene Objekt quasi seinen eigenen Gültigkeitsbereich mit. Die { ist an der Stelle, wo das throw steht, und die } ist nach dem catch(). Sehe ich das richtig?

Bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Schrompf hat geschrieben:[Hastiges Edit: sorry, ich hab grad Deinen Beitrag editiert, anstatt meine Antwort in einen neuen Beitrag zu schreiben. Ich hab jetzt hoffentlich alles wieder hergestellt, wie es vorher war]
Passt schon ^^
Schrompf hat geschrieben:Fremdcode musst Du nehmen, wie er daherkommt - als DLL oder nicht. Daher ist Dein D3D11-Beispiel schonmal hinfällig.
Stimmt doch garnicht. Wenn ich bei einem modularisierten Projekt keinen D3D11-Renderer will, lade ich sein Modul einfach nicht. Bei einem statisch gelinkten Projekt muss ich den Code verändern und neu linken, sonst lässt sich die Exe möglicherweise nicht ausführen.
Schrompf hat geschrieben:Kompiliert wird sowieso nur, was sich geändert hat - also Deine Lib, egal ob Du statisch oder dynamisch linkst.
Das ist leider Wunschdenken. Wie kommt man an den Einsprungspunkt einer statischen Lib? Bei DLLs lädt man das Modul, sucht nach dem standardisierten Namen der Factory und hat sein Interface. Bei einer statischen Lib braucht man eine Funktion, deren Name sich von Lib zu Lib unterscheiden muss, und muss diese Funktion explizit aufrufen – also auch Code des endgültigen Projekts modifizieren, wenn man ein Modul hinzufügen und entfernen möchte. Da braucht nur ein Header dazwischenzukommen und schon kompiliert alles neu.
Schrompf hat geschrieben:Die Versionsverwaltung schiebt nur geänderte Dateien hin und her - egal ob Du statisch oder dynamisch linkst.
Mit der Versionsverwaltung meinte ich, jedem Plugin einen Namen und eine Version zuordnen zu können. Ist jedes Plugin in einem eigenen Modul, ist eine Versioninfo-Ressource ein ziemlich bequemer, kompatibler und sicherer Weg, solche Informationen zu erhalten, ohne das Interface verschmutzen zu müssen.
Schrompf hat geschrieben:Und wenn Du was an einem Header Deiner Engine änderst, muss sowieso auch das abhängige Hauptprogramm neu kompiliert werden - egal, ob Du statisch oder dynamisch linkst.
Stimmt, das Argument ist eher aus hässlichen Hacks in meinem Framework heraus entstanden :)

Schrompf hat geschrieben:Weil ich keinen Überblick hatte, wie die Lebenszeit bzw. der Scope des Exception-Objekts ist, was ich da werfe. Wann es konstruiert wird, ob es kopiert wird, wann es zerstört wird. Daher wollte ich auf Nummer sicher gehen.

So, wie ich das jetzt verstehe, bringt das geworfene Objekt quasi seinen eigenen Gültigkeitsbereich mit. Die { ist an der Stelle, wo das throw steht, und die } ist nach dem catch(). Sehe ich das richtig?
Okay, ich hatte einen tieferen Sinn vermutet :) Ja, in diesem Fall stimmt das weitestgehend, der Gültigkeitsbereich endet bei der geschlossenen Klammer des catch-Blocks (es sei denn, man wirft das Objekt per throw; erneut). Allerdings ist das nicht unbedingt im Standard vorgeschrieben, das ursprünglich geworfene Objekt kann auch beliebig oft kopiert worden sein, bevor es abgefangen wird.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Helmut »

Irgendwie kauf ich dir auch nicht ab, dass die ganze Fummelei mit den DLLs wirklich produktiv ist ;) Das mit den ganzen Modulen, verschiedenen Versionen bringt, finde ich, eigentlich nur Ärger. Und wenn man dann einen beliebten Header ändert, und das passiert ja nicht gerade selten, darf wieder fast alles kompilieren.

Versuch doch mal alles, also wirklich alles, in einer einzigen CPP Datei zu kompilieren (das heißt natürlich nicht, dass du deinen ganzen Code in eine Datei packst).
Die Kompilierzeit bleibt ziemlich klein und konstant und man hat kein Stress mit DLLs. (und nebenbei wird noch etwas schnellerer Code produziert)

Ciao
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

Krishty hat geschrieben:Wenn ich bei einem modularisierten Projekt keinen D3D11-Renderer will, lade ich sein Modul einfach nicht. Bei einem statisch gelinkten Projekt muss ich den Code verändern und neu linken, sonst lässt sich die Exe möglicherweise nicht ausführen.
Nun, Du lädst tatsächlich mehr Code, wenn Du statisch linkst. Wenn Du zwischen DX10 und DX11-Renderer zur Laufzeit wechseln willst, kannst Du einfach die letzte DLL rauswerfen und eine neue stattdessen reinladen. Im statischen Fall musst Du gegen beide linken. Wenn Du dagegen zur Compile Time von DX11 auf DX10-Renderer umschalten willst, wirft der Linker sowieso alle unbenötigten Funktionen wieder raus. Dann wird die Code-Größe exakt diesselbe.

Erlaube mir aber, dieses Beispiel für albern zu erklären. Als ob Code-Größe irgendwen interessieren würde. Ganz zu schweigen von dem absurden Gedanken, überhaupt mehr als eine 3D-API pflegen zu wollen.
Schrompf hat geschrieben:Kompiliert wird sowieso nur, was sich geändert hat - also Deine Lib, egal ob Du statisch oder dynamisch linkst.
Das ist leider Wunschdenken. Wie kommt man an den Einsprungspunkt einer statischen Lib? Bei DLLs lädt man das Modul, sucht nach dem standardisierten Namen der Factory und hat sein Interface. Bei einer statischen Lib braucht man eine Funktion, deren Name sich von Lib zu Lib unterscheiden muss, und muss diese Funktion explizit aufrufen – also auch Code des endgültigen Projekts modifizieren, wenn man ein Modul hinzufügen und entfernen möchte. Da braucht nur ein Header dazwischenzukommen und schon kompiliert alles neu.
Wenn Du Deine Engine in ein Interface packst und in der Lib nur eine einzelne Funktion existiert, die Dir dieses Interface zurückgibt... dann ist das eine Funktion, egal ob dynamisch oder statisch gelinkt. Wenn sich ein Header an der Engine ändert, muss Du alle Dateien neukompilieren, die davon abhängen - egal ob der Code nachher dynamisch oder statisch dazugelinkt wird. Sorry, aber da unterscheiden sich die beiden Linkerarten einfach nicht.
Schrompf hat geschrieben:Die Versionsverwaltung schiebt nur geänderte Dateien hin und her - egal ob Du statisch oder dynamisch linkst.
Mit der Versionsverwaltung meinte ich, jedem Plugin einen Namen und eine Version zuordnen zu können. Ist jedes Plugin in einem eigenen Modul, ist eine Versioninfo-Ressource ein ziemlich bequemer, kompatibler und sicherer Weg, solche Informationen zu erhalten, ohne das Interface verschmutzen zu müssen.
Du benutzt die DLL also, um einen Teil der "Exe" nachher auszutauschen, ohne den Rest anfassen zu müssen. Meine Frage dazu wäre a) wie oft machst Du denn tatsächlich ein Update an der Engine, ohne dass das Hauptprogramm nicht auch angepasst werden müsste? und b) wieviel sparst Du dadurch am Ende? 2MB Download-Größe, weil Du mit dem nächsten Update weniger Executable zu Deinen Kunden übertragen musst?

Sorry, aber ich sehe da immernoch keinen Grund, der für mich den Programmieraufwand und den Performance-Verlust rechtfertigen würde.
Okay, ich hatte einen tieferen Sinn vermutet :) Ja, in diesem Fall stimmt das weitestgehend, der Gültigkeitsbereich endet bei der geschlossenen Klammer des catch-Blocks (es sei denn, man wirft das Objekt per throw; erneut). Allerdings ist das nicht unbedingt im Standard vorgeschrieben, das ursprünglich geworfene Objekt kann auch beliebig oft kopiert worden sein, bevor es abgefangen wird.
Ah ok, Danke für die Aufklärung.

Bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Helmut »

Achja, D3D11 kannst du doch problemlos dynamisch oder mit Delay-Loading laden.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Helmut hat geschrieben:Versuch doch mal alles, also wirklich alles, in einer einzigen CPP Datei zu kompilieren (das heißt natürlich nicht, dass du deinen ganzen Code in eine Datei packst).
Die Kompilierzeit bleibt ziemlich klein und konstant und man hat kein Stress mit DLLs. (und nebenbei wird noch etwas schnellerer Code produziert)
Die Kompilierzeit schon, die Linkzeit nicht. Schneller wird der Code kein bisschen, weil die Interfaces eh über virtuelle Funktionen laufen.
Helmut hat geschrieben:Achja, D3D11 kannst du doch problemlos dynamisch oder mit Delay-Loading laden.
Kannst du mir sagen, wie? Wenn das ohne von-Hand-Frickelei an LoadLibrary() und GetProcAddress() geht, ist das eine Bereicherung :)

Schrompf hat geschrieben:Erlaube mir aber, dieses Beispiel für albern zu erklären. Als ob Code-Größe irgendwen interessieren würde. Ganz zu schweigen von dem absurden Gedanken, überhaupt mehr als eine 3D-API pflegen zu wollen.
Mit der Code-Größe hat das überhaupt garnichts zu tun (der Code wird sogar nurnoch halb so groß beim statischen Linken, weil die ganzen gemeinsamen Funktionen gepooled werden). Was ich von Anfang an meine, ist: Link zu d3d11.lib, starte die Anwendung auf Vista ohne Platform Update oder auf XP und du kriegst die Meldung „d3d11.dll wurde nicht gefunden, Neuinstallation der Anwendung könnte das Problem beheben“ und das war’s. Unausführbar.
Schrompf hat geschrieben:Wenn Du Deine Engine in ein Interface packst und in der Lib nur eine einzelne Funktion existiert, die Dir dieses Interface zurückgibt... dann ist das eine Funktion, egal ob dynamisch oder statisch gelinkt.
Input.lib: Symbol ErzeugeInterface wurde bereits in Renderer.lib definiert. Zweite Definition ignoriert.
Audio.lib: Symbol ErzeugeInterface wurde bereits in Renderer.lib definiert. Zweite Definition ignoriert.

das meine ich. Du musst in jeder Lib eine Funktion anderen Namens haben, und jeder dieser Namen muss dem Hauptprogramm bekannt sein, bevor es dazu linken kann. (Korrigiert mich gern, falls ich da irre.) Fügst du eine Implementierung hinzu, musst du auch diesen Namen wieder bekannt machen (#include), das Programm kompilieren und linken. Bei DLLs schreibst du einfach einen neuen DLL-Namen in eine Datei, die das Programm abarbeitet. Letzteres ist viel schneller, wenn ich mein Zeug auf das Netbook ziehe, wo ich es nicht ausführen kann, weil d3d11.dll nicht gefunden wurde und Neuinstallation der Anwendung das Problem beheben könnte, oder wenn ich beim Testen der Einfachheit halber die Hälfte meiner Implementierungen rausschmeiße – was statisch immer erneutes Linken, und teils auch erneutes Kompilieren, bedeuten würde.
Du benutzt die DLL also, um einen Teil der "Exe" nachher auszutauschen, ohne den Rest anfassen zu müssen. Meine Frage dazu wäre a) wie oft machst Du denn tatsächlich ein Update an der Engine, ohne dass das Hauptprogramm nicht auch angepasst werden müsste?
Viermal am Tag, immer, wenn ich den Rechner wechsle.
Sorry, aber ich sehe da immernoch keinen Grund, der für mich den Programmieraufwand und den Performance-Verlust rechtfertigen würde.
Welcher Performance-Verlust – das Initialisieren der DLL, das selbst auf dem popeligen Netbook keine Hundertstelsekunde dauert?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von kimmi »

Helmut hat geschrieben: Irgendwie kauf ich dir auch nicht ab, dass die ganze Fummelei mit den DLLs wirklich produktiv ist ;) Das mit den ganzen Modulen, verschiedenen Versionen bringt, finde ich, eigentlich nur Ärger. Und wenn man dann einen beliebten Header ändert, und das passiert ja nicht gerade selten, darf wieder fast alles kompilieren.
Hm, wenn du bei der Änderung eines Headers alles andere neu bauen musst, solltest du dir eventuell Gedanken über deine grundlegende Architektur machen ;).
Dlls können bei sehr großen Projekten durchaus Sinn machen, bei denen der Build einige Stunden dauert. Da dann einfach eine Dll zu modifizieren geht schneller. Dort ist dann das komplette Linken ( gerade wenn man viele zyklische Abhängigkeiten in den jeweiligen Komponenten hat ) ein echter Zeitfresser. Allerdings ist das kein typischer Anwendungsfall eines Hobbyprojektes, das ist mir klar.

Gruß Kimmi
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Helmut »

Mit beliebter Header meinte ich einen Header, der von fast jeder CPP Datei inkludiert wird ;) Und dann wird jeder Compiler nochmal den ganzen Kram durchgehen, und wenn man nur nen Tippfehler korrigiert hat ;) Aber auch Header, die nur von vielleicht 3 CPPs inkludiert werden (sicher mehr als 90%), werden bei ner Änderung mehr Zeit verschlingen als bei meinem Ansatz.

Krishty, also ich würds einfach mal probieren:) Neues Projekt mit einer CPP Datei, die all deine CPP Dateien inkludet. Mit "dir *.cpp" ist das ne Sache von 5 Minuten.

Die Linkerzeit sollte sich in Grenzen halten. Jedenfalls nicht länger als jetzt. Mit der Performance hast du aber Recht. Die Linkzeitcodegenerierung ist besser als ich dachte, wie ich gerade feststellen musste :). Dauert dafür aber auch ordentlich und ist auch nur im besten Fall so gut wie mein Ansatz.

Zum Thema D3D11: Also ob du nun deine eigene DLL "mühselig" mit LoadLibrary lädst, oder die von D3D sollte wohl keinen Unterschied machen, oder? :) Aber um die 3 Extra Zeilen zu sparen gehts auch mit Delay-Loading. Sind auch nur 3 Zeilen :) Das schaltet im Grunde die Fehlermeldung am Anfang ab und wirft dafür eine Exception, wenn man eine Funktion aus der DLL aufruft. Ist also vor allem für DLLs geeignet, wo man nicht nur eine Funktion für das Interface braucht.

Ciao
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

Ich glaube, einmal probier ich es noch.
Krishty hat geschrieben:
Schrompf hat geschrieben:Erlaube mir aber, dieses Beispiel für albern zu erklären. Als ob Code-Größe irgendwen interessieren würde. Ganz zu schweigen von dem absurden Gedanken, überhaupt mehr als eine 3D-API pflegen zu wollen.
Mit der Code-Größe hat das überhaupt garnichts zu tun (der Code wird sogar nurnoch halb so groß beim statischen Linken, weil die ganzen gemeinsamen Funktionen gepooled werden). Was ich von Anfang an meine, ist: Link zu d3d11.lib, starte die Anwendung auf Vista ohne Platform Update oder auf XP und du kriegst die Meldung „d3d11.dll wurde nicht gefunden, Neuinstallation der Anwendung könnte das Problem beheben“ und das war’s. Unausführbar.
Ja, wenn eine DLL mangelt, ist das natürlich ein Problem. Dann musst Du die halt gezielt manuell laden, um das zu umgehen. Was das aber mit dem Linken gegen eine eigene Lib zu tun hat, sehe ich jetzt gerade nicht. Ich interpretiere das so: Du lädst Deine eigene DLL manuell, weil Du weißt, dass diese DLL wiederrum von D3D11 abhängt, und Du daher eine sinnvolle Fehlermeldung ausgeben kannst, wenn das Laden Deiner eigenen DLL dadurch schiefgeht. Das scheint mir zwar eine Symptombekämpfung zu sein, aber ist in der Tat ein Weg, der Dir mit statischem Linken nicht offensteht.
Schrompf hat geschrieben:Wenn Du Deine Engine in ein Interface packst und in der Lib nur eine einzelne Funktion existiert, die Dir dieses Interface zurückgibt... dann ist das eine Funktion, egal ob dynamisch oder statisch gelinkt.
Input.lib: Symbol ErzeugeInterface wurde bereits in Renderer.lib definiert. Zweite Definition ignoriert.
Audio.lib: Symbol ErzeugeInterface wurde bereits in Renderer.lib definiert. Zweite Definition ignoriert.

das meine ich. Du musst in jeder Lib eine Funktion anderen Namens haben, und jeder dieser Namen muss dem Hauptprogramm bekannt sein, bevor es dazu linken kann. (Korrigiert mich gern, falls ich da irre.) Fügst du eine Implementierung hinzu, musst du auch diesen Namen wieder bekannt machen (#include), das Programm kompilieren und linken. Bei DLLs schreibst du einfach einen neuen DLL-Namen in eine Datei, die das Programm abarbeitet. Letzteres ist viel schneller, wenn ich mein Zeug auf das Netbook ziehe, wo ich es nicht ausführen kann, weil d3d11.dll nicht gefunden wurde und Neuinstallation der Anwendung das Problem beheben könnte, oder wenn ich beim Testen der Einfachheit halber die Hälfte meiner Implementierungen rausschmeiße – was statisch immer erneutes Linken, und teils auch erneutes Kompilieren, bedeuten würde.
Gut, das sind zwei Themen. Zum Einen die Benennung. Ja, Funktionen gleichen Namens und gleicher Signatur dürfen beim Linken nicht auftreten. Dazu wurden zwar Namespaces erfunden, aber ich sag mal ehrlich: wenn Du mir DAS als Gegenargument gegen statisches Linken nennst, dann glaube ich nicht mehr daran, dass Du tatsächlich Dein DLL-Problem lösen willst. Du bist im Kopf schon lange festgelegt und willst davon nicht mehr abweichen. Ok, ist dann halt Dein Problem, aber für mich ist das wichtig, weil ich mir dann die Argumentation ersparen kann.

Zum Anderen die schnelle Austauschbarkeit von Modulen. Das ist in der Tat ein Argument. Habe ich zwar im Gegensatz zu Exceptions noch nie vermisst bisher, aber bei Dir scheint das ja an der Tagesordnung zu sein, den Renderer durch einen Nullrenderer auszutauschen. Um halt... tja, keine Ahnung, was Du damit tust.
Du benutzt die DLL also, um einen Teil der "Exe" nachher auszutauschen, ohne den Rest anfassen zu müssen. Meine Frage dazu wäre a) wie oft machst Du denn tatsächlich ein Update an der Engine, ohne dass das Hauptprogramm nicht auch angepasst werden müsste?
Viermal am Tag, immer, wenn ich den Rechner wechsle.
Du kopierst viermal am Tag die vorkompilierte DLL auf einen anderen Rechner um, um Dir dort das Kompilieren zu ersparen? Und dabei sparst Du noch was gegenüber dem Mitnehmen der ganzen Exe? Hm. Na denn viel Spaß damit.
Sorry, aber ich sehe da immernoch keinen Grund, der für mich den Programmieraufwand und den Performance-Verlust rechtfertigen würde.
Welcher Performance-Verlust – das Initialisieren der DLL, das selbst auf dem popeligen Netbook keine Hundertstelsekunde dauert?
Nein, es geht um den Performance-Verlust, den jede Funktion verursacht, die durch virtuelle Funktionen oder Funktionspointer aufgerufen wird. Mangelndes Inlining ist der größte Nachteil, bei normalen Funktionen ist der Verlust etwas geringer.

Bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von kimmi »

Schrompf hat geschrieben: Du kopierst viermal am Tag die vorkompilierte DLL auf einen anderen Rechner um, um Dir dort das Kompilieren zu ersparen? Und dabei sparst Du noch was gegenüber dem Mitnehmen der ganzen Exe? Hm. Na denn viel Spaß damit.
Ich glaube, hier herrscht ein Missverständnis vor. Also mal ausführlich: Unter Vista gibt es DX10 bzw. DX11, unter XP nicht. Wenn du die Implementierung eines Interfaces für DX9 und DX11 jeweils in einer Dll parkst, kannst du deine Engine mit beiden benutzen. Und wenn einem ein solcher Anwendungsfall wichtig ist, kann das Benutzen von Dlls durchaus ein möglicher Lösungsansatz sein ( wenn auch nicht der einzige ). Und wenn du auf beiden Kisten entwickeln willst, nimmst du die Exe nicht mit, sondern baust sie auf jeder Plattform neu.
Schrompf hat geschrieben: ...
Nein, es geht um den Performance-Verlust, den jede Funktion verursacht, die durch virtuelle Funktionen oder Funktionspointer aufgerufen wird. Mangelndes Inlining ist der größte Nachteil, bei normalen Funktionen ist der Verlust etwas geringer.
Kann ich aus eigener Erfahrung bestätigen. Executables mit statisch gelinkten Libs sind gegenüber Executables mit dynamisch geladenen Libs fixer, da sie den Lookup in der Dll nicht mehr durchführen müssen. Ich habe allerdings keine Ahnung, ob unter Windows die dynamischen Lader noch irgendwelche Optimierungen einbauen.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Helmut hat geschrieben:Krishty, also ich würds einfach mal probieren:) Neues Projekt mit einer CPP Datei, die all deine CPP Dateien inkludet. Mit "dir *.cpp" ist das ne Sache von 5 Minuten.
Entschuldige, aber das geht garnicht. Tatsächlich habe ich das nämlich bis vor ein paar Monaten so gemacht, als mein Projekt noch relativ klein war - und am Tag, als ich das auf DLLs aufgeteilt habe, hat sich die zwischendurch-kompilier-Zeit dezimiert. Wenn ich die einzelnen Module als eigene Libs kompiliere und zusammenlinke, sehe ich ein, dass das nicht viel langsamer ist als mit den DLLs. Aber alles in einem Projekt, das hatte ich schonmal und seitdem ist das ein No-Go.
Helmut hat geschrieben:Die Linkerzeit sollte sich in Grenzen halten. Jedenfalls nicht länger als jetzt. Mit der Performance hast du aber Recht. Die Linkzeitcodegenerierung ist besser als ich dachte, wie ich gerade feststellen musste :). Dauert dafür aber auch ordentlich und ist auch nur im besten Fall so gut wie mein Ansatz.
Die Link-Time-Code-Generation ist ein anderes Thema, die braucht auf meinem Netbook für alle Projekte einzeln schon zehn Minuten ... aber weil ich die nur bei Release-Builds nutze, als im eigentlichen Workflow fast nie, habe ich damit erst garnicht angefangen.
Helmut hat geschrieben:Zum Thema D3D11: Also ob du nun deine eigene DLL "mühselig" mit LoadLibrary lädst, oder die von D3D sollte wohl keinen Unterschied machen, oder? :) Aber um die 3 Extra Zeilen zu sparen gehts auch mit Delay-Loading. Sind auch nur 3 Zeilen :) Das schaltet im Grunde die Fehlermeldung am Anfang ab und wirft dafür eine Exception, wenn man eine Funktion aus der DLL aufruft. Ist also vor allem für DLLs geeignet, wo man nicht nur eine Funktion für das Interface braucht.
Das muss ich mal ausprobieren, danke :)

Schrompf hat geschrieben:Ich interpretiere das so: Du lädst Deine eigene DLL manuell, weil Du weißt, dass diese DLL wiederrum von D3D11 abhängt, und Du daher eine sinnvolle Fehlermeldung ausgeben kannst, wenn das Laden Deiner eigenen DLL dadurch schiefgeht. Das scheint mir zwar eine Symptombekämpfung zu sein, aber ist in der Tat ein Weg, der Dir mit statischem Linken nicht offensteht. [...] Zum Anderen die schnelle Austauschbarkeit von Modulen. Das ist in der Tat ein Argument. Habe ich zwar im Gegensatz zu Exceptions noch nie vermisst bisher, aber bei Dir scheint das ja an der Tagesordnung zu sein, den Renderer durch einen Nullrenderer auszutauschen. Um halt... tja, keine Ahnung, was Du damit tust.
Gut, wenn ich nicht bloß das Symptom bekämpfen soll, dann erklär mir, wie ich die Radeon 5850 in mein Netbook eingebaut kriege. Zum vierten oder fünften Mal: DER RENDERER FUNKTIONIERT AUF DEM NETBOOK NICHT. Weder ist D3D11 installiert, noch wird es von der Hardware unterstützt. Und eine Engine hat noch ganz andere Teile, an denen man auch ohne Renderer arbeiten kann ... und es gibt ja auch Dedicated Servers.
Schrompf hat geschrieben:Gut, das sind zwei Themen. Zum Einen die Benennung. Ja, Funktionen gleichen Namens und gleicher Signatur dürfen beim Linken nicht auftreten. Dazu wurden zwar Namespaces erfunden,
Was haben denn Namespaces damit zu tun? Im Code muss ich die einzelnen Funktionen doch immernoch mit unterschiedlichen Bezeichnern aufrufen, nur eben mit unterschiedlichem Namespace als Präfix, statt mit unterschiedlichem Funktionsnamen.
Schrompf hat geschrieben:aber ich sag mal ehrlich: wenn Du mir DAS als Gegenargument gegen statisches Linken nennst, dann glaube ich nicht mehr daran, dass Du tatsächlich Dein DLL-Problem lösen willst. Du bist im Kopf schon lange festgelegt und willst davon nicht mehr abweichen. Ok, ist dann halt Dein Problem, aber für mich ist das wichtig, weil ich mir dann die Argumentation ersparen kann.
Stimmt halb und halb. Ich habe mich nie gegen statische Libs entschieden, habe extra alles konfiguriert, damit ich das Projekt jederzeit zwischen dynamischer und statischer Linkage umstellen kann. Du willst aber, dass ich DLLs komplett aufgebe und das ist nicht drin - weil man externe Module irgendwann brauchen wird. Und es ist mir lieber, wenn ich jetzt das Exception-Handling aller Schnittstellen umschreiben muss, als wenn ich das in zwei Jahren machen muss, wenn eine DLL unvermeidbar ist und ich zehntausende Zeilen umschreiben muss, um das wieder ins Reine zu kriegen.
Schrompf hat geschrieben:Du kopierst viermal am Tag die vorkompilierte DLL auf einen anderen Rechner um, um Dir dort das Kompilieren zu ersparen? Und dabei sparst Du noch was gegenüber dem Mitnehmen der ganzen Exe? Hm. Na denn viel Spaß damit.
Ich schmeiße SyncToy an, gehe zum Netbook, klappe es zu, klappe es im Zug wieder auf und arbeite. Statisch müsste ich erst den Renderer deaktivieren, neu kompilieren, ... aber mal gucken, wie es mit Delay-Loading ist.
Schrompf hat geschrieben:Nein, es geht um den Performance-Verlust, den jede Funktion verursacht, die durch virtuelle Funktionen oder Funktionspointer aufgerufen wird. Mangelndes Inlining ist der größte Nachteil, bei normalen Funktionen ist der Verlust etwas geringer.
Okay, natürlich sind dynamisch gelinkte Funktionen und virtuelle Funktionen generell immer langsamer. Dass das nur in meinem Fall marginal ist (durch die Interfaces marschiert der Code-Fluss höchstens zehnmal pro Frame), hätte ich dazusagen sollen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4865
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Schrompf »

Krishty hat geschrieben: Du willst aber, dass ich DLLs komplett aufgebe und das ist nicht drin - weil man externe Module irgendwann brauchen wird. Und es ist mir lieber, wenn ich jetzt das Exception-Handling aller Schnittstellen umschreiben muss, als wenn ich das in zwei Jahren machen muss, wenn eine DLL unvermeidbar ist und ich zehntausende Zeilen umschreiben muss, um das wieder ins Reine zu kriegen.
Das ist wohl der Kernpunkt der Diskussion. DLLs sind nicht unvermeidbar. Ganz im Gegenteil - ich sehe keinen Grund, der den ganzen Ärger mit verschiedenen Heaps und sonstigen Abweichungen wert wäre. Du dagegen denkst, dass Du sowieso irgendwann DLLs einsetzen *musst* und deswegen auch gleich damit anfangen kannst. Dein weiteres Vorgehen ist dann einfach nur eine konsequente Umsetzung dieses Grundsatzes.

Dabei würde ich es dann belassen.

Bye, Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: [C++] Exceptions und Module

Beitrag von odenter »

Ihr habt Probleme. :)

Wenn ein "rebuild all" eurer Hobby-Projekte über einer Stunde liegt, dann habt Ihr ein völlig anderes Problem. Nur mal so zum Vergleich ein Ogre oder Irrlicht wird auf meinem Rechner (Windows 7, Intel E8500 und 4GB Ram) in weniger als 20 Minuten gebaut.

wxWidgets baue ich in allen Konfigurationen auch in unter einer Stunde. ^^

Ansonsten machen *.dlls halt da Sinn wo man dinge dynamisch austauschen will, sagt ja auch schon der Name "dynamic linked library".
Schreib doch einfach wofür Du meinst dlls zu brauchen, willst Du ein sauberes Interface für Deine Renderer bauen und die Renderer-Implementierungen (DX, OpenGL, Software) als *.dll daherkommen lassen? Für sowas kann man das ja auch gut gebrauchen.

Was ich nicht machen würde wäre z.B. Code mit irgendwelchem Mathe zeugs in eine "Mathe.dll" zu stecken oder das Fehlersystem in eine "Fehler.dll" etc. das würde ich zusammen lassen, gehört ja auch zusammen.

EDIT:
Da z.B. Irrlicht und auch Ogre Exceptions werfen und statisch und dynamisch erzeugt werden können, scheint es also grundsätzlich kein Problem zu sein. Passt auch zu dem hier:
http://www.gamedev.net/community/forums ... =1&#908452

EDIT die 2:
*.dlls machen natürlich auch Sinn wenn der Programm-Code sehr groß ist und man gerne einzelne Teile davon austauschen möchte ohne immer alles komplett zu patchen, wobei dieses Problem in Zeiten von DSL ja nicht mehr sooo riesig ist.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Habe momentan wenig Zeit, darum konnte ich die Zusammenfassung zum Thread noch nicht posten … ich arbeite einfach mal das Aktuellste ab.
odenter hat geschrieben:Wenn ein "rebuild all" eurer Hobby-Projekte über einer Stunde liegt, dann habt Ihr ein völlig anderes Problem. Nur mal so zum Vergleich ein Ogre oder Irrlicht wird auf meinem Rechner (Windows 7, Intel E8500 und 4GB Ram) in weniger als 20 Minuten gebaut.

wxWidgets baue ich in allen Konfigurationen auch in unter einer Stunde. ^^
Ist ja schön und gut, die arbeiten aber auch sicher nicht mit Dimensionsanalyse und diesem ganzen Kram. Hat man viel Compiler-generated-Code, zieht sich das Ganze eben auch entsprechend hin. Außerdem sind die Kompilier- und Linkzeiten ja in erster Linie auf meinem Netbook ein Problem – das hat nur einen Kern mit Hyper-Threading, also werden aus deinen 20 Minuten schonmal 30. Getaktet ist es 3× langsamer, noch dazu nur in-Order, winziger RAM, langsame Festplatte, schon sind aus den 20 Minuten zwei Stunden geworden. Und das nur ohne Energiesparmodus, aber dann ist bei der Belastung auch schon der Akku leer.
odenter hat geschrieben:Da z.B. Irrlicht und auch Ogre Exceptions werfen und statisch und dynamisch erzeugt werden können, scheint es also grundsätzlich kein Problem zu sein. Passt auch zu dem hier:
http://www.gamedev.net/community/forums ... =1&#908452
Natürlich ist das Problem grundsätzlich. Dass es bei Irrlicht und Ogre nicht auftritt kann tausend Gründe haben – vielleicht wandern Exceptions nicht bis zur Wurzel des Call-Stacks zurück, vielleicht verlassen die sich bei der Freigabe der Module nicht auf RAII sondern machen es von Hand, vielleicht benutzen sie eigene Exceptions ohne virtuelle Datentypen und Speicherallokierung, vielleicht existiert das Problem ja doch und es ist nur bisher niemandem aufgefallen (letzteres trifft definitv auf den Thread zu), wahrscheinlich kennen sie es und haben es schon lange umgangen. Ich kenne deren Architekturdetails und Quellcodes zu schlecht, um das beurteilen zu können. Aber das Problem an sich existiert und ist wohl nicht sauber lösbar.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Aramis »

Unabhängig von dem heiligen DLLvsStatic-Krieg: wie oft tritt denn der Fall auf, dass Du wenn eine Funktion eine Exception wirfst gleich da ganze Modul aus deinem Adressraum rauswirfst? Richtig, genau einmal. Nämlich in der einen Initialisierungsfunktion die dir Zugriff auf die in der DLL implementierten Interfaces gibt. Wenn deine in der DLL implementierte Renderer::MachWas() fehlschlägt, wirst du wohl nicht ganz so rabiat reagieren wollen.. . Das Problem mit den virtuellen Destruktoren ist nicht lösbar, also sind Exceptions hier schlichtweg fehl am Platz. Seit Urzeiten gibt es dafür Rückgabewerte.

Wenn dieser Fall für dich häufiger auftritt bzw. seine korrekte Behandlung für dich essentiell ist, dann ist (C++-)Exception-Handling nicht das richtige Tool um das Problem zu lösen, unabhängig von seiner Eleganz. Der eleganteste Code ist immer noch der, der die richtigen Tools für den richtigen Zweck einsetzt :-)
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Aramis hat geschrieben:Das Problem mit den virtuellen Destruktoren ist nicht lösbar, also sind Exceptions hier schlichtweg fehl am Platz. Seit Urzeiten gibt es dafür Rückgabewerte.

Wenn dieser Fall für dich häufiger auftritt bzw. seine korrekte Behandlung für dich essentiell ist, dann ist (C++-)Exception-Handling nicht das richtige Tool um das Problem zu lösen, unabhängig von seiner Eleganz. Der eleganteste Code ist immer noch der, der die richtigen Tools für den richtigen Zweck einsetzt :-)
Das ist eine Antwort, thx :)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: [C++] Exceptions und Module

Beitrag von odenter »

Hier ist die Header-Datei der Ogre-Exceptions, vielleicht hilft Dir das ja.
http://www.ogre3d.org/docs/api/html/Ogr ... ource.html

EDIT:
So war neugierig und hab das gerade mal kurz ausprobiert und in meinen alten Win32 Büchern nachgeguckt. Sofern ich in der *.dll die Exception mit

Code: Alles auswählen

throw std::exception("bla blubb")
werfe funzt alles einwandfrei

Code: Alles auswählen

throw new std::exception("bla blubb")
geht natürlich nicht, wurde ja schon mehrfach geschrieben, da angeforderter Speicher aus einem Modul nicht in einem anderen freigegeben werden darf. Deshalb gibt man Win32-API Funktionen auch den Speicher jeweils mit. Das sollte man also unbedingt vermeiden,vermutlich hast Du genau das versucht, mit Deiner Exception Klasse.

Ich vermute mal hier gibt es einfach ein Design-Problem, denn Objekte in c++ über DLL-Grenzen zu geben geht nur :

1.)
Wenn die *.exe und die *.dll mit dem gleichen Compiler (Version) und den gleichen Settings gebaut wurde. (deshalb geht mein Testcode)

2.)
C-Style API, ala Win32-API (Bsp.:CreateWindow und DestroyWindow) wo der angeforderte Speicher auch wieder im Modul freigegeben wird.

3.)
Oder ein sauberes Design mit Interfaces (abstrake-Klassen, pure virtual und keine Daten-Member) ala COM.

Übrigens hab ich semi-Bullshit geschrieben, wxWidgets und Irrlicht werfen keine Exceptions. Allerdings ist es in wxWidgets mitlerweile möglich Exceptions zu werfen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8261
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Exceptions und Module

Beitrag von Krishty »

Dein Versuch ist doppelt falsch, ich bin mir ziemlich sicher, dass du das Problem nicht verstanden hast.

Zum einen allokiert ::std::exception seinen what()-String doch ebenfalls auf dem Heap. Ob du die Exception per new schmeißt oder nicht, macht ergo nur eine Allokation mehr oder weniger aus, dynamisch allokiert wird aber so oder so.

Zum anderen wette ich, dass du die DLL nicht per RAII verwaltet hast. Das Problem ist ja nicht, dass die Exceptions nicht ankommen – das Problem ist, dass die Exceptions, wenn sie ankommen, auf Speicher und Funktionstabellen der DLL verweisen obwohl diese schon freigegeben wurde. Der Speicher des Strings in der Exception und ihre virtuellen Funktionen sind definitiv ungültig und können nicht benutzt werden. Es ist eine Unmöglichkeit, dass das Programm nicht abstürzt, wenn du es so machst.

Der Klarheit halber (exemplarisch runtergehackt):

Code: Alles auswählen

// TestModule.dll
WINAPI void TestFunction() {
	throw ::std::exception("BOOM");
}


// Test.exe

class CDLL {
private:
	::HMODULE m_Handle;
public:

	explicit CDLL(
		::std::wstring const & p_ItsFilename
	)
		: m_Handle(::LoadLibraryW(p_ItsFilename.c_str()))
	{
		if(nullptr == this->m_Handle)
			throw runtime_error_from_GetLastError("LoadLibraryW");
	}

	::FARPROC Function(
		::std::string const & p_ItsName
	) const {
		::FARPROC l_Function = ::GetProcAddress(this->m_Handle, p_ItsName.c_str());
		if(nullptr != l_Function)
			return l_Function;
		else
			throw runtime_error_from_GetLastError("GetProcAddress");
	}

	~CDLL() throw() {
		::FreeLibrary(this->m_Handle);
	}

}; // class CDLL


void main(int argc, char * argv[]) {
	try {
		CDLL l_TestModule(L"TestModule.dll");
		l_TestModule.Function("TestFunction")();
	}
	catch(::std::exception const & e_) {
		::std::cout << e_.what(); // Hier kollabiert das bekannte Universum.
		// Der vftable von e_ verweist auf Speicher der DLL, ebenso die Strings mit den Fehlermeldungen. Die DLL wurde aber wieder freigegeben, da der D’tor von l_TestModule vor dem catch aufgerufen wurde.
	}
}
Ogre verwendet ebenfalls virtuelle Funktionen und dynamisch allokierten Speicher.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: [C++] Exceptions und Module

Beitrag von odenter »

Ja weil

Code: Alles auswählen

CDLL l_TestModule(L"TestModule.dll");
innerhalb des try-Blockes ist, bei verlassen ist es halt futsch.
Schieb das doch über den try Block ggf. mit nem boolean Rückgabewert ob das laden geklappt hat oder nicht. Dann sollte es doch gehen.
Antworten