Was sind die Vor- und Nachteile der Verwendung von Ausnahmen in C ++ in Bezug auf die Spieleentwicklung ?
Laut Google Style Guide werden Ausnahmen aus verschiedenen Gründen nicht verwendet. Sind die gleichen Gründe für die Spieleentwicklung relevant?
Wir verwenden keine C ++ - Ausnahmen ... - google-styleguide.googlecode.com
Einige Fragen zum Nachdenken.
- Wie es sich um die Entwicklung einer Bibliothek handelt, die in mehreren Projekten verwendet wird.
- Wie wirkt es sich auf Unit-Tests, Integrationstests usw. aus?
- Was bedeutet es, Bibliotheken von Drittanbietern zu verwenden?
c++
software-engineering
David Young
quelle
quelle
Antworten:
Es gibt auch einige schlimme Konsequenzen für die Verwendung von Ausnahmen in C ++, wenn Sie nicht alle Details ihrer Funktionsweise kennen. Da viele Spiele in C ++ geschrieben sind, kann dies ein Faktor sein.
Beispielsweise kann das Auslösen einer Ausnahme in einem Destruktor in C ++ schwerwiegende Folgen haben. Es kann alle Arten von undefiniertem Verhalten und Abstürzen verursachen, da Sie in einer Situation gefangen sein können, in der Sie mehr als eine aktive Ausnahme im Flug haben. ( Weitere Informationen hierzu finden Sie unter Punkt 8 von Effective C ++ .)
quelle
Joel über die Ansichten von Software zu Ausnahmen.
Interessante Ansicht derzeit in keiner der Antworten,
http://www.joelonsoftware.com/items/2003/10/13.html
quelle
Ausnahmen werden in mindestens einer modernen Konsolenentwicklungsumgebung nicht unterstützt und daher dringend empfohlen.
Die meisten Konsolenentwickler, die ich kenne, ziehen es vor, sie sowieso nicht zu verwenden, da die ausführbare Datei zusätzlichen Aufwand verursacht und die Philosophie, dass sie einfach nicht benötigt werden. RTTI wird genauso gesehen.
quelle
Von allen Projekten, an denen ich in Spielen gearbeitet habe, verwendete keines Ausnahmen. Der Funktionsaufruf-Overhead ist der Hauptgrund. Wie bereits erwähnt, ist es wie RTTI in den meisten Studios kein Diskussionsthema. Nicht, weil die meisten Programmierer einen Java / Akademiker-Hintergrund haben, denn ehrlich gesagt, die meisten nicht.
Es wirkt sich nicht wirklich auf Unit-Tests aus, da Sie nur die Bedingungen festlegen. Bei Bibliotheken ist es ausnahmslos besser, wenn Sie es jedem aufzwingen, der es verwendet.
Während es einige Situationen etwas schwieriger macht, damit umzugehen, zwingt es Sie (imo) auch zu einer kontrollierten Einrichtung, da Ausnahmen zu Missbrauch führen können. Fehlertoleranz ist nett, aber nicht, wenn sie zu schlampiger Codierung führt.
Wohlgemerkt, das kommt von jemandem, der nie die Ausnahmebehandlung verwendet hat (okay, ein- oder zweimal in Tools - es hat es nicht für mich getan, ich denke, es ist das, womit Sie aufgewachsen sind).
quelle
function();
Sie nicht sofort wissen, ob ein Fehlercode ignoriert wird oder nicht. Dies ist vermutlich der Grund, warum Sprachen, mit denen Sie einen Rückgabewert ignorieren können, versuchen, Sie dazu zu zwingen, Ausnahmen zu bevorzugen.Error
, basiert auf einer Klasse, die leichtgewichtig ist. Wenn sie ignoriert wird, wird eine Zusicherung ausgelöst. Es kann also wirklich nicht ignoriert werden, nicht mehr als eine Ausnahme kann ignoriert werden.Das Wichtigste für mich als professioneller Spieleentwickler ist, dass Ausnahmen von Leuten geschrieben werden müssen, die verstehen, wie Ausnahmen funktionieren (was es in der Spielebranche nur wenige gibt), die auch von ihnen debuggt werden, und den zugrunde liegenden Code, der sie ausführt (was ist) Ein verzweigtes Durcheinander, das Ihren Prozessor in der richtigen Reihenfolge tötet, musste von Leuten geschrieben werden, die Ihren Compiler / Linker bereitstellen. Das Problem dabei ist, dass sie nur eine begrenzte Menge an Ressourcen haben, also konzentrieren sie sich darauf, bessere Optimierungen und Korrekturen für Code zu schreiben, den Spieleentwickler verwenden ... was normalerweise keine Ausnahmen sind. Wen werden Sie anrufen, wenn Sie einen Ausnahmefehler bei moderner Hardware mit neuen Compilern haben? Ihr Ausnahmeexperte, der der Meinung ist, dass mit dem Code alles in Ordnung ist, oder der Anbieter, der sagt: Ja, bald, wir '
Mit Ausnahmen ist nichts an sich falsch, nur sind sie nicht gut, weil die Realität der Theorie im Wege steht.
quelle
Für mich kann die Ausnahmebehandlung großartig sein, wenn alles RAII-konform ist und Sie Ausnahmen für wirklich außergewöhnliche Pfade reservieren (z. B. eine beschädigte Datei, die gelesen wird), und Sie niemals Modulgrenzen überschreiten müssen und Ihr gesamtes Team an Bord ist ......
Zero-Cost EH
Die meisten Compiler implementieren heutzutage eine kostengünstige Ausnahmebehandlung, die es sogar billiger machen kann als die manuelle Verzweigung unter Fehlerbedingungen in regulären Ausführungspfaden, obwohl im Gegenzug außergewöhnliche Pfade enorm teuer werden (es gibt auch einige Code-Aufblähungen, wenn auch wahrscheinlich nicht mehr als wenn Sie jeden möglichen Fehler gründlich manuell behandelt hätten). Die Tatsache, dass das Werfen enorm teuer ist, sollte jedoch keine Rolle spielen, wenn Ausnahmen so verwendet werden, wie sie in C ++ vorgesehen sind (für wirklich außergewöhnliche Umstände, die unter normalen Bedingungen nicht auftreten sollten).
Umkehrung von Nebenwirkungen
Trotzdem ist es viel einfacher gesagt als getan, alles an RAII anzupassen. Es ist für lokale Ressourcen, die in einer Funktion gespeichert sind, einfach, sie in C ++ - Objekte mit Destruktoren zu verpacken, die sich selbst bereinigen, aber es ist nicht so einfach, eine Rückgängig- / Rollback-Logik für jeden einzelnen Nebeneffekt zu schreiben, der in der gesamten Software auftreten kann. Überlegen Sie nur, wie schwierig es ist, einen Container zu schreiben,
std::vector
der die einfachste Sequenz mit wahlfreiem Zugriff darstellt, um absolut ausnahmesicher zu sein. Multiplizieren Sie diese Schwierigkeit nun über alle Datenstrukturen einer gesamten großen Software.Betrachten Sie als grundlegendes Beispiel eine Ausnahme, die beim Einfügen von Elementen in ein Szenendiagramm auftritt. Um diese Ausnahme ordnungsgemäß zu beheben, müssen Sie möglicherweise diese Änderungen im Szenendiagramm rückgängig machen und das System wieder in einen Zustand versetzen, in dem der Vorgang nie stattgefunden hat. Das bedeutet, dass die in das Szenendiagramm eingefügten untergeordneten Elemente automatisch entfernt werden, wenn eine Ausnahme auftritt, möglicherweise von einem Scope Guard.
Dies gründlich in einer Software zu tun, die viele Nebenwirkungen verursacht und mit vielen dauerhaften Zuständen umgeht, ist viel einfacher gesagt als getan. Oft denke ich, dass die pragmatische Lösung darin besteht, sich nicht darum zu kümmern, um die Dinge zu versenden. Mit der richtigen Art von Codebasis, die über ein zentrales Rückgängig-System verfügt, und möglicherweise dauerhaften Datenstrukturen und der minimalen Anzahl von Funktionen, die Nebenwirkungen verursachen, können Sie möglicherweise auf ganzer Linie Ausnahmesicherheit erreichen ... aber es ist eine Menge Dinge, die Sie brauchen, wenn Sie nur versuchen wollen, Ihren Code überall ausnahmesicher zu machen.
Dort stimmt praktisch etwas nicht, da die konzeptionelle Umkehrung von Nebenwirkungen ein schwieriges Problem darstellt, das in C ++ jedoch schwieriger wird. Manchmal ist es tatsächlich einfacher, Nebenwirkungen in C als Reaktion auf einen Fehlercode umzukehren, als als Reaktion auf eine Ausnahme in C ++. Einer der Gründe ist, dass jeder Punkt einer Funktion möglicherweise
throw
in C ++ vorhanden sein kann. Wenn Sie versuchen, einen generischen Container zu schreiben, der mit type arbeitetT
, können Sie nicht einmal vorhersehen, wo sich diese Austrittspunkte befinden, da alles,T
was dazu gehört, werfen könnte. Sogar ein VergleichT
untereinander für Gleichheit könnte werfen. Ihr Code muss alle Möglichkeiten bewältigen , und die Anzahl der Möglichkeiten multipliziert sich in C ++ exponentiell, während sie in C viel weniger sind.Fehlende Standards
Ein weiteres Problem der Ausnahmebehandlung ist das Fehlen zentraler Standards. Es gibt einige, denen hoffentlich alle zustimmen, dass Destruktoren niemals werfen sollten, da Rollbacks niemals fehlschlagen sollten, aber das deckt nur einen pathologischen Fall ab, der die Software im Allgemeinen zum Absturz bringen würde, wenn Sie gegen die Regel verstoßen.
Es sollte vernünftigere Standards geben, wie niemals von einem Vergleichsoperator werfen (alle Funktionen, die logisch unveränderlich sind, sollten niemals werfen), niemals von einem Verschiebungs-ctor werfen usw. Solche Garantien würden es so viel einfacher machen, ausnahmesicheren Code zu schreiben. Wir haben jedoch keine solchen Garantien. Wir müssen uns an die Regel halten, dass alles werfen könnte - wenn nicht jetzt, dann möglicherweise in der Zukunft.
Schlimmer noch, Menschen mit unterschiedlichem Sprachhintergrund betrachten Ausnahmen sehr unterschiedlich. In Python haben sie tatsächlich diese "Sprung vor dem Blick" -Philosophie, bei der Ausnahmen in regulären Ausführungspfaden ausgelöst werden! Wenn Sie dann solche Entwickler dazu bringen, C ++ - Code zu schreiben, könnten Sie nach Ausnahmen suchen, die für Dinge, die nicht wirklich außergewöhnlich sind, nach links und rechts geworfen werden, und alles zu handhaben kann ein Albtraum sein, wenn Sie versuchen, generischen Code zu schreiben, z
Fehlerbehandlung
Die Fehlerbehandlung hat den Nachteil, dass Sie nach jedem möglichen Fehler suchen müssen, der auftreten könnte. Es hat jedoch den Vorteil, dass Sie im Nachhinein manchmal etwas spät nach Fehlern suchen können
glGetError
, um beispielsweise die Austrittspunkte in einer Funktion erheblich zu reduzieren und sie explizit zu machen. Manchmal können Sie den Code etwas länger ausführen, bevor Sie einen Fehler überprüfen, weitergeben und beheben, und manchmal ist dies wirklich einfacher als die Behandlung von Ausnahmen (insbesondere bei Umkehrung von Nebenwirkungen). Aber vielleicht beschäftigen Sie sich auch nicht so sehr mit Fehlern in einem Spiel, sondern schließen das Spiel möglicherweise nur mit einer Meldung, wenn Sie auf beschädigte Dateien, Speicherfehler usw. stoßen.Ein unangenehmer Teil von Ausnahmen in DLL-Kontexten ist, dass Sie Ausnahmen nicht sicher von einem Modul auf ein anderes werfen können, es sei denn, Sie können garantieren, dass sie von demselben Compiler erstellt werden, dieselben Einstellungen verwenden usw. Wenn Sie also wie eine Plugin-Architektur schreiben Ausnahmen, die für Benutzer aller Arten von Compilern und möglicherweise sogar aus verschiedenen Sprachen wie Lua, C, C #, Java usw. verwendet werden sollen, werden häufig zu einem großen Ärgernis, da Sie jede Ausnahme verschlucken und in Fehler übersetzen müssen Codes sowieso überall.
Wenn es sich um Dylibs handelt, können sie aus den oben genannten Gründen keine Ausnahmen sicher auslösen. Sie müssten Fehlercodes verwenden, was auch bedeutet, dass Sie bei Verwendung der Bibliothek ständig nach Fehlercodes suchen müssen. Sie könnten ihre Dylib mit einer statisch verknüpften C ++ - Wrapper-Bibliothek umschließen, die Sie selbst erstellen und die Fehlercodes aus der Dylib in ausgelöste Ausnahmen übersetzt (im Grunde genommen aus Ihrer Binärdatei in Ihre Binärdatei). Im Allgemeinen denke ich, dass die meisten Bibliotheken von Drittanbietern sich nicht einmal darum kümmern sollten und sich nur an Fehlercodes halten sollten, wenn sie Dylibs / Shared Libs verwenden.
Ich treffe im Allgemeinen nicht so oft auf Teams, die außergewöhnliche / fehlerhafte Pfade ihres Codes testen. Früher habe ich es getan und hatte tatsächlich Code, von dem
bad_alloc
ich mich richtig erholen konnte, weil ich meinen Code extrem robust machen wollte, aber es hilft nicht wirklich, wenn ich der einzige bin, der es in einem Team tut. Am Ende hörte ich auf zu stören. Ich stelle mir für eine unternehmenskritische Software vor, dass ganze Teams prüfen könnten, ob ihr Code in allen möglichen Fällen zuverlässig von Ausnahmen und Fehlern wiederhergestellt wird, aber das ist viel Zeit für Investitionen. Die Zeit macht in unternehmenskritischer Software Sinn, aber vielleicht kein Spiel.Hassliebe
Ausnahmen sind auch meine Art von Liebes- / Hass-Funktion von C ++ - eine der wirkungsvollsten Funktionen der Sprache, die RAII eher zu einer Anforderung als zu einer Annehmlichkeit macht, da es die Art und Weise ändert, wie Sie Code verkehrt herum schreiben müssen , C, um die Tatsache zu behandeln, dass jede gegebene Codezeile ein impliziter Austrittspunkt für eine Funktion sein könnte. Manchmal wünschte ich mir fast, die Sprache hätte keine Ausnahmen. Manchmal finde ich Ausnahmen wirklich nützlich, aber sie sind für mich ein zu dominierender Faktor, wenn ich einen Code in C oder C ++ schreibe.
Sie wären für mich exponentiell nützlicher, wenn sich das Standardkomitee wirklich stark auf die Festlegung von ABI-Standards konzentrieren würde, die es ermöglichen, Ausnahmen sicher über Module hinweg zu werfen, zumindest von C ++ bis C ++ - Code. Es wäre großartig, wenn Sie sicher über Sprachen hinwegwerfen könnten, obwohl das eine Art Wunschtraum ist. Die andere Sache, die Ausnahmen für mich exponentiell nützlicher machen würde, ist, wenn
noexcept
Funktionen tatsächlich einen Compilerfehler verursachten, wenn sie versuchten, Funktionen aufzurufen, die ausgelöst werden könnten, anstatt nurstd::terminate
auf eine Ausnahme zu stoßen. Das ist so gut wie nutzlos (könnte das genauso gut nennen,icantexcept
anstatt tatsächlich einen Compilerfehler zu verursachen, wenn der Destruktor irgendetwas getan hat, das werfen könnte. Wenn das zu teuer ist, um es zur Kompilierungszeit gründlich zu bestimmen, dann machen Sie es einfach sonoexcept
). Es wäre viel einfacher sicherzustellen, dass alle Destruktoren ausnahmesicher sind, zum Beispiel, wennnoexcept
noexcept
Funktionen (die alle Destruktoren implizit sein sollten) dürfen nur ebenfallsnoexcept
Funktionen / Operatoren aufrufen und es zu einem Compilerfehler machen, von einem von ihnen zu werfen.quelle
Meines Erachtens gibt es zwei Hauptgründe, warum es in der Spieleentwicklung traditionell nicht verwendet wird:
quelle