Strings in Exceptions
-
Roger Wilco schrieb:
Trotzdem mal so als "Umfrage": Wie macht Ihr das?
wie Dravere.
-
So, nun habe ich bei mir aufgeräumt und die Exceptions auf String-Literale umgestellt.
Das reicht auch in den meisten Fällen. Nun habe ich aber doch schon Stellen wo ich mir einen dynamischen String herbeisehne bzw. nicht weiß, ob ich einen falschen Ansatz verfolge. Wäre nett, wenn Ihr mir nochmal unter die Arme greift:
Ich lese eine Konfiguration ein. Dabei werden oft Parametersätze geladen, die teilweise auch aus indizierten Parametern bestehen.
Simples Beispiel:
ParamSet Config::ReadParamSet(){ ParamSet paramSet; do{ paramSet.Add(GetParam(index)); } while(/*noch nicht alle Parameter eingelesen*/); return paramSet; } Param Config::GetParam(unsigned int paramIndex){ // Hier wäre size_t angebracht, oder? Param param; // Parameter wird ermittelt... if(/*Parameter nicht gefunden?*/){ throw ParamNotFound(); // Hier möchte ich sagen, // welcher Parameter genau (Index) // nicht gelesen werden konnte } return param; }Entweder ich übergebe im Konstruktor von ParamNotFound ein std::wstring oder ich leite mir von ParamNotFound eine neue Klasse "ParamXyZNotFound" ab, die ein Parameter-Index im Konstruktor enthält.
Ich benötige das zum Loggen, um sagen zu können welcher Parameter genau fehlt. Ich könnte natürlich auch gleich an Ort und Stelle den Fehler loggen und dann einen allgemeinen ParamNotFound werfen.
Was sagt Ihr dazu?
-
Prinzipiell würde ich Draveres-Variante nehmen, nur würde das bedeuten, dass ich quasi pro Parametertyp eine eigene Exception-Klasse benötige, um den Parameter zu identifizieren.
1.) Würdet ihr das so machen?
class ParamANotFound : public ParamNotFound{/*...*/}; // beinhaltet Parameter-A spezifische Daten class ParamBNotFound : public ParamNotFound{/*...*/}; // beinhaltet Parameter-B spezifische Daten class ParamCNotFound : public ParamNotFound{/*...*/}; // beinhaltet Parameter-C spezifische Daten2.) Oder Logging an Ort und Stelle, wo die Informationen bereit stehen und dann einfach die Aufrufer über ein ParamNotFound informieren? Den Aufrufer interessiert ja eh nicht, welcher einzelner Parameter nun nicht gelesen werden konnte. Er weiß "Ich bekomme keine Daten/habe keine vollständigen Daten".
-
Hi,
also ich muss gestehen, dass ich std::strings in Exceptions ungemein praktisch finde - und deswegen nicht komplett auf sie verzichte.
Ja - ich weiß um die Gefahren, aber gerade in so Situationen wie Deine (Wilco), überwiegen in meiner Einschätzung die Vorteile von std::string (bzw. die Nachteile einer überbordenden "Exceptionklasserei" oder mangelhafter Aussagekraft) in vielen Fällen die Gefahr von fliegenden Exceptions im std::string-Konstruktor.Ich baue allerdings den string vorher zusammen (üblicherweise via ostringstream) und kopiere ihn anschließend im Exceptionkonstruktor, so dass eine fliegende Exception beim "Stringzusammenbau" noch unter meine Kontrolle fällt.
Gruß,
Simon2.
-
Und Speicher fuer Text sollte auf deinem Home Computer heutzutage immer vorhanden sein. Demnach ... mache dir deswegen erstmal keine Gedanken.
Ich kenn Applikation wo dem nicht zwingend so ist. Da werden in 20s mal 2GB vollgeschrieben.
-
Wenn ja, wie macht Ihr das: via std::string?
Ja, sieht so aus:
class MyException : public std::exception { public: explicit MyException(char const* blabla) { m_text.reset(new std::string(blabla ? blabla : "")); } virtual char const* what() const { if (m_text) return m_text->c_str(); else return "(unknown)"; } private: boost::shared_ptr<std::string> m_text; };Wenn beim Konstruieren der ursprünglichen Exception ein std::bad_alloc fliegt, ist das egal. Das hat nur zur Folge, dass eine andere Exception fliegt - eben bad_alloc statt was auch immer wir gerade werfen wollte.
Das "Rumreichen" (=Copy-Construction/Assignment) ist no-throw, und das ist es worauf es ankommt.
-
Danke, diese Lösung finde ich gut.
Baust Du Deine Exception-Klassen grundsätzlich nach diesem Schema auf?
-
Wenn man statt von std::exception von den exceptions aus <stdexcept> ableitet (z.B. logic_error oder runtime_error), dann kann man denen problemlos einen String als Konstruktorargument übergeben. Beispiel aus dem Quellcode zu meinem aktuellen Artikel:
struct DivisionDurchNullException : public std::domain_error { explicit DivisionDurchNullException(std::string const& what_arg) : std::domain_error(what_arg) {} }; //... if (n == 0) throw DivisionDurchNullException("Division durch Null beim Erstellen eines Rational-Objektes."); //...Hier ist es ähnlich wie bei hustbaers Lösung: wenn beim erzeugen des Strings ein bad_alloc fliegt, dann geschieht das bevor die Exception existiert.
-
Wobei diese Exceptions den std::string einfach kopieren und als private Member mitführen. Zumindest in der stdexcept meiner IDE (Visual C++ 6 *bäh*).
-
Roger Wilco schrieb:
Wobei diese Exceptions den std::string einfach kopieren und als private Member mitführen.
Was wiederum zeigt dass die Hersteller deiner Standardbibliothek auch die Auffassung vertreten "wenn die String-Kopie schiefläuft ist eh alles hoffnungslos".
Zumindest in der stdexcept meiner IDE (Visual C++ 6 *bäh*).
Wenn NSVC 6 bäh ist, wieso legst du dir keine aktuellere IDE zu? (z.B. MSVC 2008 Express)
-
Zumindest in der stdexcept meiner IDE (Visual C++ 6 *bäh*).
Wenn NSVC 6 bäh ist, wieso legst du dir keine aktuellere IDE zu? (z.B. MSVC 2008 Express)
Weil die Personen, denen ich (indirekt) die schwarzen Zahlen auf meinem Kontoauszug zu verdanken habe, das anders sehen wie ich.

Außerdem vertraue ich einer Standardbibliothek, die vor Erscheinen des Standards erstellt wurde, nicht sonderlich. Vor allem bei einer so bekannterweise mangelhaften Umsetzung.
Wenn ich das anspreche, bekomme ich eh wieder zu Hören, dass ich doch MFC nutzen soll, weil das vieeeeel besser sei und viel ausgereifter/mächtiger/intuitiver als die Standardbibliothek... *stöhn*
Ich soll eigentlich auch Klassen-C schreiben, statt OO, weil das viel einfacher/intuitiver/ausgereifter ... (Ich muss mich auch jedesmal überwinden, das vorgeschriebene "Klassen-C-Prefix" zu benutzen. Den lpcwstrVariablenNamen-Blödsinn konnte ich immerhin erfolgreich abwehren.) *stöhn*
-
Roger Wilco schrieb:
...das anders sehen wie ich....
nooooaaa.... was ist denn das?

@Topic: Mein herzliches Beileid!!
Kannst Deine "Entscheider" ja mal hier vorbeischicken.Gruß,
Simon2.
-
@Roger Wilco,
In eine Exception kommt keine Fehlerbeschreibung, sondern nur Fehlerdaten. Den String, welchen man übergibt, sollte meiner Meinung nach eher einem Titel oder Betreff gleichen.Wenn du einen Index durchreichen willst, dann reich auch den Index durch. Wandle ihn sicher nicht vor dem durchreichen in einen String um. Wieso erweiterst du nicht gleich
ParamNotFound, um eine Möglichkeit einen Index zu übergeben? Oder wird die Exception auch noch für etwas anderes verwendet? Wenn dem so ist, dann machst du halt noch eineIndexedParamNotFound, welche einen Index bekommt.Die Exception ist nur für den Fehler verantwortlich, der Exceptionwerfer nur für die Fehlerbenachrichtigung, die Fehlerbeschreibung oder was auch immer am Ende mit dem Fehler passieren soll, darum hat sich der Fänger zu kümmern.
Und im übrigen: Mein Beileid, dass du solche Arbeitsgeber hast

Grüssli
-
@RogerWilco, da hilfts nur eins: Informier dich, bilde dich weiter und sei gut in dem was du tust, und zwar so dass die Schlipsträger irgendwann schnallen dass du davon mehr Ahnung hast als sie.
-
Simon2 schrieb:
Kannst Deine "Entscheider" ja mal hier vorbeischicken.
*hehe* Wenn ich bei Problemen vorschlage (was ich manchmal tue) mal im Internet und in Foren wie dieses hier nachzusehen oder mal nachzufragen, dann ist die Antwort:
"Das sind nur Freaks! Machen immer aus einer Mücke einen Elefanten und schießen mit Kanonen auf Spatzen! Das geht alles viel einfacher und unkomplizierter! Wir schauen nochmal in der (mitgelieferten!) MSDN nach (zum 1000x und finden wieder keine Lösung)!" (zwar kein O-Ton, aber der Inhalt stimmt wirklich!)
Aber genug Off-Topic...
@Dravere: So sehe ich das eigentlich auch, und wollte den Index eben haben, um (später) den Fehler zu beschreiben (loggen). Nur wusste ich nicht, ob es sinnig ist, für jeden Parametertyp eine eigene Exception-Klasse abzuleiten.
Ihr erkennt aufgrund Eurer Erfahrung viel schneller, wenn irgendwas in Richtung Design-Fehler läuft, daher fragte ich.
-
pumuckl schrieb:
@RogerWilco, da hilfts nur eins: Informier dich, bilde dich weiter und sei gut in dem was du tust, und zwar so dass die Schlipsträger irgendwann schnallen dass du davon mehr Ahnung hast als sie.
Mit dieser Aussage wäre ich vorsichtig. Gerade wenn diese eingestaubten Meinungen zur Programmierung herrschen. Manchmal ist es in so einer Firma sinnvoller sich weiterzubilden und dann eine andere Firma zu suchen (Wenn Leistungen und Fähigkeiten nicht in irgendeiner Weise gewürdigt werden [das muss nicht zwangsweise Geld sein], braucht eine Firma einen auch nicht).
cu André
-
Roger Wilco schrieb:
@Dravere: So sehe ich das eigentlich auch, und wollte den Index eben haben, um (später) den Fehler zu beschreiben (loggen). Nur wusste ich nicht, ob es sinnig ist, für jeden Parametertyp eine eigene Exception-Klasse abzuleiten.
Was ist ein Parametertyp? Wie sieht das Design aus? Es ist ziemlich schwer hier zu sagen, ob man mehrere Klassen erstellen sollte oder nicht.
Im allgemeinen sagt man aber, nicht zu viel und nicht zu wenig
Oft kann man viele Informationen als Variablen in eine Exception packen. Sagt zum Beispiel der Index nicht schon etwas über den Parametertyp aus? Wie kann man einen Parametertyp bezeichnen? Womöglich ein zusätzliches Stringliteral? Und die wichtige Frage: Ist es überhaupt wichtig zu wissen, welcher Parametertyp es war?Bemerkung am Rande: Unterschätzt niemals Freaks!

Grüssli
-
Roger Wilco schrieb:
Wobei diese Exceptions den std::string einfach kopieren und als private Member mitführen. Zumindest in der stdexcept meiner IDE (Visual C++ 6 *bäh*).
VC6 verwendet AFAIK COW für std::string. Was zwar grundsätzlich schlecht ist, aber es ermöglicht, no-throw Kopien von Strings zu machen. Sogesehen ist das bei MSVC 6 auch kein Problem.
Und ab 7.1/8 (die kein COW mehr für std::string verwenden) sind die Exception Klassen dann auch anders implementiert.
-
Dravere schrieb:
Was ist ein Parametertyp? Wie sieht das Design aus?
Mein "ConfigReader" ist eine Fabrik, die mir unterschiedliche Objekte liefert, die Daten aus einer Config-Datei oder Registry benötigen.
Vereinfachtes Beispiel, das dem Prinzip aber entspricht:
Parameter "LKW" (mit Bierkisten): Klasse LKW, Klasse Bierkasten, Klasse Bierflasche.
config.ini: [LKW_1] AnzahlBierkisten=3 FlaschenInBierkiste_1= 12 // Bin kein Biertrinker, was gibt es da für Größen? FlaschenInBierkiste_2= 24 FlaschenInBierkiste_3= & // *Ups* da hat sich wohl einer vertippt... Biersorte=Lieblingsbier [LKW_2] ...Innerhalb der Fabrik, werden die einzelnen Parameter-Objekte über Fabrikmethoden geladen. Ich möchte nun in meiner Fehlerbeschreibung stehen haben:
... ERROR: Konfigurationsfehler: LKW 1: Viel zu wenig Bier geladen!
Ach Quatsch...
... ERROR: Konfigurationsfehler: LKW 1/Bierkiste 3/Anzahl Bierflaschen: Parameter nicht vorhanden oder fehlerhaft!
So in der Art...
Dravere schrieb:
Unterschätzt niemals Freaks!

ICH tue das bestimmt nicht! Verstehe noch lange nicht alles hier im Forum und habe schon viel (mehr wie im Studium, würde ich behaupten) über sinniges C++ Programmieren gelernt! Vielen Dank an dieser Stelle für die ausgezeichnete Unterstützung! 
-
Mal deine Fehlermeldung auseinandernehmen:
... ERROR: Konfigurationsfehler: LKW 1/Bierkiste 3/Anzahl Bierflaschen: Parameter nicht vorhanden oder fehlerhaft! | 1 | 2 | 3 | 4 |- Teil 1: Das kann der Fänger dazufügen.
- Teil 2:
int lkw-> könnte man über eine Variable lösen. - Teil 3:
int bierkiste-> könnte man über eine Variable lösen. - Teil 4: Title/Betreff, also durch ein Stringliteral lösbar. Könnte man vielleicht noch weiter unterteilen.
Das ist vielleicht möglich, hängt alles von deinem Code ab und was du alles genau wie rausparst.
Allerdings würde ich die Sache sowieso ein wenig anders anpacken. Das Configfile muss einer bestimmten Grammatik folgen, nehme ich an. Ich würde daher eher das ganze auseinander nehmen, um es danach besser bearbeiten oder lesen zu können und dabei die Grammatik prüfen. Somit dann eher einen Gramatikfehler werfen mit Zeilen- und Zeichennummer.
Wenn die Grammatik stimmt und das File entsprechend zerlegt wurde, dann kannst du anfangen die Konfiguration auf deine Klassen zu übertragen.Ist auch alles ein wenig relativ und kommt ganz auf den entsprechenden Fall an

Und ich will hier jetzt auch nicht völlig gegenstd::stringin einer Exception sein, man sollte halt nur bei der Verwendung vonstd::stringaufpassen, dass man die Fehlerbeschreibung nicht am falschen Ort generiert, also dass die Aufgaben korrekt getrennt sind. Das einstd::stringeine Exception wirft, ist ziemlich unwahrscheinlich und wenn, dann ist es meistens "egal", wie hier schon mehrfach gesagt wurde.Grüssli