Was ist der standardkonforme Weg zu entscheiden, was von was () von einer Klasse zurückgegeben wird, die von std :: system_error geerbt wurde, ohne Daten zu duplizieren?

8

Ich verwende eine Klasse, die von std::system_error für die Fehlerbehandlung geerbt wurde, und ich möchte kontrollieren, was zurückgegeben wird, wenn what() aufgerufen wird. Grund: Der Standard (sowohl C ++ 11 als auch der Entwurf C ++ 1y CD - N3690, § Verweise unten sind in letzterer) gibt nicht an, wie genau die von what() zurückgegebene Zeichenkette aussehen soll, sondern nur eine Anmerkung in § 19.5.6.2 (14):

  

Hinweis: Das zurückgegebene NTBS könnte der Inhalt von what_arg + ": " + code.message() sein. - Endnote

daher wird es als implementierungsabhängig betrachtet. (Übrigens, sollte es nicht code().message() anstelle von code.message() sein?)

Die Frage ist also: Wie kann ich genau die Zeichenfolge definieren, die what() zurückgibt, wenn ich standardkonform sein will und nicht auf die Implementierung angewiesen bin (d. h. portierbar sein will)?

Für diejenigen, die den Code bevorzugen:

%Vor%

Ok, eine triviale Lösung, die ich nicht mag, könnte die folgende sein:

%Vor%

Da std::exception::what() virtuell ist, wird es funktionieren, aber gibt es einen eleganteren Weg, ohne Implementierungsdetails zu verwenden? Ich mag die Idee nicht, zwei Strings gespeichert zu haben: eine in std::system_error und die andere in my_what .

Die Wurzel des Problems: std :: runtime_error - was zufällig die Elternklasse von std :: system_error ist - hat eine genaue Bedingung in §1.9.2.6 (3), eine Nachbedingung des Konstruktors:

%Vor%

Was bei std::system_error in §19.5.6.2 (2) zu folgendem wird:

%Vor%

Hat jemand eine Ahnung, warum der Standard so sehr versucht, code().message() in what() einzubeziehen? Beachten Sie, dass code() das Fehlercode-Objekt zurückgibt, sodass jeder code().message() in die Zeichenfolge jederzeit einfügen kann (selbst zu dem Zeitpunkt, zu dem eine Ausnahme dieser Klasse abgefangen wird).

Wenn die Anforderung von std::system_error die gleiche wie die von std::runtime_error ist, könnte ich einfach schreiben:

%Vor%

Gibt es eine elegante und tragbare Lösung?

UPDATE: Viele der folgenden Kommentare geben an, dass die Fehlermeldungen in der Implementierung definiert sind. Ich verstehe, dass ich nur die Zeichenfolge formatieren , die von what() zurückgegeben wird, nicht auf allen Systemen byteweise äquivalent sein soll. Denken Sie nur daran, dass ich es protokollieren oder an eine dritte Partei weitergeben möchte und dass es einem festen Format folgt (was nicht vom Standard vorgeschlagen wird).

UPDATE2: Ich glaube, dass std :: system_error nicht nur für OS oder STL-Fehler ist. Ich kann (und nehme an) daraus eigene Klassen ableiten und sie für die Fehlerberichterstattung verwenden. Was ist, wenn ich eine Low-Level-API schreibe? Übrigens, warum ist es verboten, es in einer High-Level-API zu verwenden?

Wenn ich alle Argumente an den Konstruktor im Fehlerbehandlungsteil meiner API übergebe, sind keine Implementierungen definiert (dh unbekannte) Fehlerzeichenfolgen, aber ich kann sie immer noch nicht ohne Duplizieren formatieren Daten.

    
Norbert Bérci 03.07.2013, 21:28
quelle

2 Antworten

6

Ich kenne keine "elegante und portable" Lösung, aber ich denke nicht, dass mit der Lösung, die Sie in der Frage vorschlagen, etwas falsch ist, nämlich Ihre eigene Kopie von what_arg zu erstellen.

Das Exception-Objekt wird nicht lange dauern: normalerweise nur lange genug, um den Stack abzuwickeln. Außerdem ist die Zeichenfolge in what_arg wahrscheinlich nicht sehr lang, da ein Megabyte von what nicht gut lesbar ist. Und schließlich wird das Ausnahmeobjekt nur in Ausnahmefällen erstellt, so dass die "unnötige" Duplizierung einer kleinen Zeichenfolge keine merklichen Auswirkungen auf die Leistung Ihres Programms hat.

Im Grunde genommen haben die Klassenentwickler den Status der Klasse undurchsichtig gemacht, und - um es ganz offen zu sagen - Sie vertrauen nicht darauf, dass sie aus diesem Zustand ein brauchbares Ergebnis (eine lesbare Nachricht) erzeugen. In diesem Fall müssen Sie den Status duplizieren. Dies ist eine nicht seltene Konsequenz der Zustandsverkapselung, und die allgemeine Lösung besteht fast immer darin, den Zustand zu duplizieren. In diesem Fall sind die Kosten gering. Wenn Sie also den Wert von what() für wichtig halten, sollten Sie die Kosten akzeptieren und weitermachen.

    
rici 04.07.2013, 07:14
quelle
2

Ich möchte einen längeren Kommentar abgeben, also werde ich dies als Community-Wiki-Antwort veröffentlichen.

Der Standard (C ++ 14 Draft N3690) beschreibt system_error in [syserr.syserr.overview] / 1:

  

Die Klasse system_error beschreibt ein Ausnahmeobjekt, das zum Melden von Fehlerbedingungen mit einem zugehörigen Fehlercode verwendet wird. Solche Fehlerbedingungen stammen normalerweise vom Betriebssystem oder anderen Low-Level-Anwendungsprogrammschnittstellen.

Fehler und Fehlermeldungen, die vom Betriebssystem oder anderen Low-Level-APIs stammen, sind notwendigerweise nicht portierbar .

Wenn Sie außerdem von system_error ableiten, erwartet jemand, der die Ausnahme durch ref abfängt, die Fehlercodes von den OS / Low-Level-APIs, wie von der C ++ - Implementierung angegeben; zumindest weiß dieser Benutzer, dass das Ergebnis von what() vom Standard nicht garantiert wird. Wenn dieser Benutzer eine Ausnahme von Ihrem abgeleiteten Typ abfängt, können Sie auch eine andere what2() -Funktion bereitstellen, die genau Ihre Zeichenfolge zurückgibt.

Die Lösung, die ich vorschlagen möchte, ist nicht , um von system_error abzuleiten. Soweit ich das beurteilen kann, ist Ihre Analyse der Garantien (und des Tippfehlers code().message() ) korrekt, d. H. Sie können die Rendite von what() für system_error nicht genau angeben. Wenn Sie in Ihrer Ausnahme einen Fehlercode haben möchten, können Sie die error_code -Funktionen verwenden; Wie in system_error spezifiziert der Standard jedoch in [syserr] / 1

  

Dieser Abschnitt beschreibt Komponenten, die die Standardbibliothek und C ++ - Programme verwenden können, um Fehlerbedingungen zu melden, die vom Betriebssystem oder anderen Low-Level-Anwendungsprogrammschnittstellen stammen.

Daher ist es möglicherweise besser, einen anderen Fehlercodetyp zu verwenden.

Soweit ich sehen kann, ist die Ableitung von system_error nützlich, wenn Sie die Fehlermeldung von system_error s, die von den OS / Low-Level-APIs stammt, verbessern möchten. In diesem Fall möchten Sie möglicherweise eine Beschreibung zur Nachricht hinzufügen ( what() ), und das ist system_error garantiert für die an den ctor übergebene Nachricht.

Noch ein "Kommentar", um zu beantworten, wie Sie von system_error ableiten und die Rückgabe von what() genau spezifizieren können:

%Vor%

Ja, es gibt Datenduplizierung in dem Sinne, dass es (wahrscheinlich) zwei String-Objekte gibt, aber es gibt keine Datenduplizierung des Inhalts der Zeichenkette. Wenn Sie keine Zeichenfolge an den ctor der Ausnahme übergeben müssen (z. B. der Typ der Ausnahme und der Fehlercode sind beschreibend genug), können Sie sogar das Zeichenfolgenobjekt auslassen. etwas wie

%Vor%

Obwohl es wahrscheinlich besser ist, den Fehlercode in die Beschreibung einzufügen, die von what() zurückgegeben wird (erfordert eine Art von Speicher in my_class ).

Es könnte sich lohnen zu erwähnen, dass die Konstruktion der Zeichenkette on demand (d. h. wenn what() aufgerufen wird) günstig sein könnte.

    
DyP 05.07.2013 17:28
quelle

Tags und Links