Exception werfen
-
unskilled schrieb:
boar... es geht um exceptions - ich weiß ja nicht, wie ihr programmiert, aber bei mir ist es eher die ausnahme, dass exceptions fliegen - also muss ich mir da echt 0 gedanken um performance machen - weil das ohnehin meist heißt, dass irgendwas nicht geht/ging...
Kein Mensch redet von performance...
@Xebov:
Eine string klasse mit minimalen speicher verbrauch ist immer nuetzlich - und sie zu schreiben ist weniger aufwendig als bei jeder exception klasse immer die ganzen operatoren zu definieren.komponenten wie zB strings oder aehnliches zu schreiben ist idR deutlich weniger aufwand als die funktionalitaet immer doppelt und dreifach zu schreiben

-
Shade Of Mine schrieb:
Kein Mensch redet von performance...
@Xebov:
Eine string klasse mit minimalen speicher verbrauch ist immer nuetzlich - und sie zu schreiben ist weniger aufwendig als bei jeder exception klasse immer die ganzen operatoren zu definieren.komponenten wie zB strings oder aehnliches zu schreiben ist idR deutlich weniger aufwand als die funktionalitaet immer doppelt und dreifach zu schreiben

Ja das is natürlich ein Argument. Wobei Copykonstruktor und operator alles nur durchreichen, die Arbeit selbst macht allein der Copykonstruktor/operator der Basisklasse. Ich werds aber mal in Betrachtziehen da ich in etlichen klassen nur einfache CStrings speichern muß, da könnte ne Kapselung nicht schaden.
unskilled schrieb:
boar... es geht um exceptions - ich weiß ja nicht, wie ihr programmiert, aber bei mir ist es eher die ausnahme, dass exceptions fliegen - also muss ich mir da echt 0 gedanken um performance machen - weil das ohnehin meist heißt, dass irgendwas nicht geht/ging...
Es geht dabei weniger um Performance, wenn ich generell Exceptions werfe und welche dabei sind die OutOfMemory bedeuten ist es schon vorteil haft in diesem Fall sowenig Speicher wie Möglich zu holen um zu vermeiden das das erstellend er Exception selbst fehlschlägt.
-
Xebov schrieb:
Ich wolte unnötiges Speicherholen vermeiden. Hab mich mit den strings mal auseinandergesetzt, und gelesen das für die ausgabe das Strings als CString nochmal Speicher geholt wird um den CString zu bauen, ...
Verstehe ich dich richtig, dass du nicht wolltest, wenn du die Funktion
std::string::c_str()verwendest, dass hier zusätzlicher Speicher verbraucht wird? Ich hoffe, dir ist bewusst, dass die meistenstd::stringImplementationen hier nicht zusätzlichen Speicher allokieren, sondern den eigenen Speicher intern immer um +1 grösser halten und eine '\0' am Ende reinspeichern.Das was du hier machst, würde ich als PMO bezeichnen. Du probierst etwas zu optimieren, wo du noch gar keine Ahnung hast, ob es wirklich schlechter läuft.
Grüssli
-
Dravere schrieb:
Verstehe ich dich richtig, dass du nicht wolltest, wenn du die Funktion
std::string::c_str()verwendest, dass hier zusätzlicher Speicher verbraucht wird? Ich hoffe, dir ist bewusst, dass die meistenstd::stringImplementationen hier nicht zusätzlichen Speicher allokieren, sondern den eigenen Speicher intern immer um +1 grösser halten und eine '\0' am Ende reinspeichern.So wie ich das mal verstanden hatte garanteirt der std:string doch nicht das die zeichen in der Reihenfolge eines CStrings im Speicher liegt und erstellt den CString nur bei bedarf? Oder war das Blödsinn?
-
Shade Of Mine schrieb:
unskilled schrieb:
boar... es geht um exceptions - ich weiß ja nicht, wie ihr programmiert, aber bei mir ist es eher die ausnahme, dass exceptions fliegen - also muss ich mir da echt 0 gedanken um performance machen - weil das ohnehin meist heißt, dass irgendwas nicht geht/ging...
Kein Mensch redet von performance...
wenn man performance nicht nur über laufzeit definiert, sondern auch speicherverbrauch mitzählt, dann schon!
@Xebov: Ja, der Standard garantiert das nicht - aber ich habe bisher noch nie eine Implementation gesehen, wo der string beim c_str() umkopiert wird... ist ja auch nicht wirklich sinnvoll...
und trotzdem ist eine solche klasse ab und an ganz hilfreich - aber das ist und bleibt eine exception - eine ausnahme - was bringt es dir, wenn dein programm in ausnahmen weit unter 1kb speicher weniger braucht?
bb
-
Xebov schrieb:
So wie ich das mal verstanden hatte garanteirt der std:string doch nicht das die zeichen in der Reihenfolge eines CStrings im Speicher liegt und erstellt den CString nur bei bedarf? Oder war das Blödsinn?
Ich frage mich nach wie vor, wer für die Speicherverwaltung zuständig sein sollte, wenn der
std::stringeinen neuen C-String erstellt.Xebov, hattest du wirklich mal eine OutOfMemoryException? Aufgrund welcher Gegebenheiten wirfst du diese? In C++ ist für zu wenig Speicher eigentlich
std::bad_alloczuständig, ansonsten hat deine Exception vielleicht nicht den richtigen Namen...
-
Nexus schrieb:
Xebov, hattest du wirklich mal eine OutOfMemoryException? Aufgrund welcher Gegebenheiten wirfst du diese? In C++ ist für zu wenig Speicher eigentlich
std::bad_alloczuständig, ansonsten hat deine Exception vielleicht nicht den richtigen Namen...Hatte noch nie, meine Programme verbrauchen noch viel zu wenig Speicher als das das jemals passieren könnte. Ich werfe sie wenn ich in einigen programmteilen std::bad_alloc auffange, ich gliedere die Exception quasi ein, ich fange sie auf und werfe meine eigene an der Stelel weiter, aber nicht imemr, außerdem verwende ich sie für DX Fehlercodes die das selbe aussagen, wieso fragst du?
-
Xebov schrieb:
wieso fragst du?
Weil es mich wundert, dass du dir die Mühe machst, eine fast nie auftretende Exception wie
std::bad_allocnoch selbstständig weiterzuleiten. Ganz konsequent wirst du das wahrscheinlich nicht machen können...Hast du etwas gegen die Exception-Hierarchie der Standardbibliothek?
-
Nexus schrieb:
Xebov schrieb:
wieso fragst du?
Weil es mich wundert, dass du dir die Mühe machst, eine fast nie auftretende Exception wie
std::bad_allocnoch selbstständig weiterzuleiten. Ganz konsequent wirst du das wahrscheinlich nicht machen können...Hast du etwas gegen die Exception-Hierarchie der Standardbibliothek?
Nein ich habe dagegen nichts. Ich bastel ne kleine Engine zusammen und die kommt halt aus einem Guß nun habe ich aber 2 verschiedene OutOfMemory Meldungen, einmal nen fehelrcode aus DX und einmal ne std::bad_alloc aus der std, und die leite ich beide auf eine eigene OutOfMemory Exception um.
Konsequent bin ich dabei ind er Tat nicht, da sis aber absicht, ich leite std::bad_alloc nur da um wo es sinn macht, dh Hauptsächlich wenn sich Interne Daten vergrößern, wenn aber Hauptteile "hochfahren" dann leite ich sie nicht um weil dann das Programm so oder so nicht weit käme.
-
Nexus schrieb:
[...Ich frage mich nach wie vor, wer für die Speicherverwaltung zuständig sein sollte, wenn der
std::stringeinen neuen C-String erstellt...Na
std::string. Du bekommst überc_str()einenchar const*zurück, der nur so lange gültig ist, wie es der String selbst ist. Es bleibt ja nach wie vor ein Puffer, dessen Eigentümer das String-Objekt ist. Selbst, wenn intern eine Kopie erzeugt wird.
-
unskilled schrieb:
wenn man performance nicht nur über laufzeit definiert, sondern auch speicherverbrauch mitzählt, dann schon!
Nein...
Es geht hier um Exceptions - die treten in Ausnahmefällen auf. Hier einen optimalen Memory Footprint zu haben ist common sense und keine optimierung.
Idealerweise würde man nämlich 0 bytes allokieren, aber das ist nicht immer möglich.
-
Tachyon schrieb:
Na
std::string. Du bekommst überc_str()einenchar const*zurück, der nur so lange gültig ist, wie es der String selbst ist. Es bleibt ja nach wie vor ein Puffer, dessen Eigentümer das String-Objekt ist. Selbst, wenn intern eine Kopie erzeugt wird.Danke, das scheint Sinn zu machen. Ich war bis jetzt eher davon ausgegangen, dass in dem Falle, wo
c_str()nicht der internen Repräsentation entspricht, ein eigenständiges Objekt zurückgegeben würde. Aber falls dieser Fall überhaupt vorkommt, gehört die Speicherverwaltung also dem String...
-
Tachyon schrieb:
Na
std::string. Du bekommst überc_str()einenchar const*zurück, der nur so lange gültig ist, wie es der String selbst ist. Es bleibt ja nach wie vor ein Puffer, dessen Eigentümer das String-Objekt ist. Selbst, wenn intern eine Kopie erzeugt wird.Das hatten wir doch letztens schon einmal. Wie soll die Klasse sich um die Speicherverwaltung kümmern, wenn der c_str()-Aufruf das Objekt nicht verändern darf? Es geht mit Mörder-Frickelei und ausgelagerten Datenstrukturen, ja, aber nicht mit einer auch nur halbwegs praktikablen Lösung.
-
Badestrand schrieb:
...
c_str()muss das Objekt nicht ändern. Sollte hinterc_str()tatsächlich ein anderer Puffer stehen als hinter dem Stringpuffer selbst, so muss dieser bei jeder Ändererung des Stringpuffers mitgeändert werden. Nicht erst beim Aufruf vonc_str().c_str()selbst hat dabei nichts zu melden. Die Funktion muss immer nur einen konstanten Zeiger auf die aktuelle C-String-Repräsentation des Strings zurückgeben.
Und den (separaten) Puffer im Destruktor vonstd::basic_stringzu zurstören beisst sich nicht im geringsten mit der constness vonc_str().
Irgendwelche "ausgelagerten Datenstrukturen" sind dabei völlig überflüssig.
-
Also ich habe im Moment folgende Exception struktur und weiß nicht so recht ob das Beispiel am Anfang von Dravere sinnvoll für mich wäre:
//irgendwo in main try { try { // in diesem Teil wird das hauptprogramm sozusagen angstoßen mit // allen sub_routinen(). foo(); } catch (std::runtime_error &err) { std::cerr << err.what() << std::endl; // es folgt noch einiges zum exit } } catch (std::bad_alloc & err) { std::cerr << err.what() << std::endl; // es folgt noch einiges zum exit }in meinem foo() und allen subroutinen gibt es dann bis jetzt immer nur immer ein
throw std::runtime_error("Hier steht dann meine Fehlermeldung");Ich überlege jetzt die eigene exceptionsklasse von dravere zu übernehmen jedoch weiß ich nicht genau ob es sinn machen würde...ich habe schließlich doch nur lauf-zeitfehler oder nicht? Und wenn dann unterscheidet sich doch z.B. auch ein fehler bei division durch Null nur durch die message die geworfen wird. Wo ist der Unterschied? Ist meine bisherige struktur eher Blödsinn und wenn ja wie würdet ihr das schreiben?
-
Kommt halt drauf an, wie stark du abstrahieren und unterscheiden willst. Wenn du sowieso alle Fehler gleich behandelst (kann ja sein), lohnt es sich auch nicht, für jeden Fehlertypen eine neue Klasse zu schreiben. Wenn du aber zum Beispiel noch zusätzliche Informationen in den Exceptions mittragen willst, kann sich eine eigene Klasse bewähren, da man da halt viel freier ist.
Warum machst du das so umständlich? Ich meine jetzt nicht mal die Einrückung, sondern die Verschachtelung der
trys. Machs doch einfach so:try { // Fehleranfälliger Code } catch (std::runtime_error& e) { // Behandle Laufzeitfehler } catch (std::bad_alloc& e) { // Behandle Allokationsfehler }
-
ah das wußte ich noch nicht.
Nur noch mal ne frage. Allein nur an der message würde sich was ändern...also der string sozusagen. Da machts doch wenig sinn da ne exception klasse mit unterschiedlichen exceptions zu bauen oder?
-
gambo schrieb:
Ich überlege jetzt die eigene exceptionsklasse von dravere zu übernehmen jedoch weiß ich nicht genau ob es sinn machen würde...ich habe schließlich doch nur lauf-zeitfehler oder nicht? Und wenn dann unterscheidet sich doch z.B. auch ein fehler bei division durch Null nur durch die message die geworfen wird. Wo ist der Unterschied? Ist meine bisherige struktur eher Blödsinn und wenn ja wie würdet ihr das schreiben?
Schreiben siehe Nexus post.
Was den Sinn angeht, ich halte verschiedene Erbende Exceptions für durchaus sinnvoll. Der Punkt daran ist der du weißt was fliegt, du kannst passende aussagekräftige Klassennamen nehmen. Der Vorteil daran ist außerdem der, wenn du mit deinen nachrichten Exceptions wirfst, dann fliegt nur eine, aber du mußt die Strings immer vergleichen, hast du aber Exceptionklassen dann weißt du anhand der Klasse genau was Sache ist und kannst direkt loslegen, außerdem gibt das den catch Blöcken etwas mehr Struktur.
throw std::runtime_error("Device Lost"); throw std::runtime_error("Datei nicht gefunden"); throw e_MyException_Device("Device (hier Name eintragen) Lost"); throw e_MyException_File("Datei (hier Name eintragen) nicht gefunden");Hier müsstest du oben imer die gleiche Nachricht nutzen, wärend du unten siehst was der Fehler ist und die Nachricht damit individuelelr sien kann, was sieht schöner aus?
-
Tachyon schrieb:
Die Funktion muss immer nur einen konstanten Zeiger auf die aktuelle C-String-Repräsentation des Strings zurückgeben.
Genau, zusammenhängendend und mit '\0' am Ende.
Wenn nicht mit mutable, externen oder static-Sachen gebastelt wird, also c_str() keine neue Kopie eines Buffers anfertigen kann (weil dann ja keiner aufräumen kann), muss hier
string s = "foo"; char& c = s[2]; // (1) c = '.'; // (2) s.c_str(); // (3)bei (1) schon eine Referenz auf ein Element eines zusammenhängenden Null-terminierten Buffers zurückgegeben werden. Schließlich bekommt die string-Instanz nicht mit, wenn ich bei (2) etwas ändere, muss aber immer einen Null-terminierten String mit den Änderungen bereithalten. Und das geht eben nur, wenn direkt in den zusammenhängenden, Null-terminierten String geschrieben wird, der bei c_str() zurückgegeben wird.
-
gambo schrieb:
} catch (std::runtime_error &err) { std::cerr << err.what() << std::endl; // es folgt noch einiges zum exit }exit ist in so einer Situation nicht unbedingt der Weisheit letzter Schluß. Statische Objekte werden so nicht mehr destruiert und das Programm auf die harte Tour beendet, da kann man gleich abort() oder terminate() aufrufen.
Man sollte aus dem Hauptprogramm mittels "return EXIT_FAILURE;" rausgehen, das ist sauber und ordentlich.
So sähe das besser aus:int main () { try { foo (); } catch (std::exception &e) { ... return EXIT_FAILURE; } catch (...) { ... return EXIT_FAILURE; } }