C++ Datei schließen ?



  • volkard schrieb:

    hustbaer schrieb:

    file.close() wegzulassen ist grundsätzlich eine Unart.

    Nö.
    Das Problem ist grundsätzlich ungelöst.

    Nö.

    volkard schrieb:

    Was machste mit zwei Files in zwei unterschiedlich tiefen Funktionen?

    Beim tieferen (inneren) close() fliegt ne Exception.
    Dadurch wird das äussere File vom Dtor geschlossen. Was effektiv ein "abort" ist, also ein "versuchs bitte aber ignorier' alle Fehler" Schliessen.
    Der Inhalt beider Files ist potentiell kaputt. Aber - sofern das OS kein kompletter Schrott ist: beide Handles wurden freigegeben, beide Files sind also "geschlossen".

    Irgendwo wird die Exception gefangen und dem User angezeigt. Jetzt weiss er dass die Files möglicherweise Schrott enthalten.

    => Problem gelöst.

    volkard schrieb:

    close() muss einfach klappen.

    Und was wenn nicht? Geht dann die Welt unter?
    => Unsinn.

    Es geht nicht darum zu garantieren dass immer alles gut geht. Das kann man nicht. Es geht darum zu garantieren dass man nur dann "haben fertig, alles OK" sagt, wenn auch wirklich alles OK ist. Und das ist nicht grundsätzlich ungelöst, sondern sogar sehr einfach zu lösen. Nur halt nicht "sehr einfach" mit den iostreams, weil sie den falschen Default-Wert für die Exceptions-Maske haben.



  • cpp_beginner_offl schrieb:

    ? So :

    file.flush;
    
    if (!file.fail){
         file.close();
    }
    

    ?? Oder wie meinst du das?

    Nein.
    Eher so:

    file.flush();
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    Bzw. auch gerne als

    if (!file.flush())
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    geschrieben.

    Nach dem flush() kann man das File dann ruhig über den Dtor schliessen lassen. Alle Änderungen wurden ja bereits geschrieben, d.h. es gibt im Dtor nix mehr zu tun was schief gehen könnte.
    Oder anders gesagt: es können keine Fehler mehr passieren von denen man erfahren wollen würde.



  • Ich hab mal kurz in der Onlinerefernz zu flush nachgelesen...

    http://www.cplusplus.com/reference/ostream/ostream/flush/

    Ok, Return Value *this, darum prüfst du auf (!file)...
    Es steht aber, wenn ich auf die schnelle richtig übersetzt habe , auch drinn, das verschiedene Failstates gesetzt werden.

    Würde

    if (file.fail() ){
        throw std::runtime_error ("Sth. really bad happened...");
    }
    

    genauso funktionieren oder ist es "sicherer" auf (!file) zu prüfen ?



  • Laut http://www.cplusplus.com/reference/ios/ios/operator_not/ ist !stream equivalent zu fail()



  • Hab selbst grad noch ein wenig nachgelesen/rumprobiert.
    Noch besser wäre gleich close aufzurufen und danach den Stream-State zu prüfen.
    Also

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    close setzt nämlich im Falle des Falles auch das fail Bit, und das Prüfen des Stream-States scheint auch nach dem Schliessen des Files erlaubt zu sein.
    => Besser als die flush() Variante, da man keine Vermutung mehr darüber anstellen muss ob close nach erfolgreichem flush noch fehlschlagen kann/darf oder nicht.


  • Mod

    Dir ist schon klar, dass jetzt viele Leute diesen Code wortwörtlich kopieren werden und dann stolz meinen werden, dass

    throw std::runtime_error("Waaaaaaaah, MIST!");
    

    eine bessere Art der Fehlerbehandlung wäre, als das ganze Thema einfach zu ignorieren und gar kein close zu setzen?



  • Also wer die Fehlermeldung 1:1 übernimmt, dem kann ich auch nicht helfen.

    Gibt es vom Text "Waaaaaaaah, MIST!" abgesehen noch etwas zu kritisieren?

    ps: Schlag ne bessere Fehlermeldung vor, und ich kopier' sie rein (bzw. fühl dich frei es selbst reinzueditieren wenn du es für wichtig bzw. sinnvoll erachtest).


  • Mod

    hustbaer schrieb:

    Also wer die Fehlermeldung 1:1 übernimmt, dem kann ich auch nicht helfen.

    Gibt es vom Text "Waaaaaaaah, MIST!" abgesehen noch etwas zu kritisieren?

    Ich meine eher, dass das reine Werfen der Exception in diesem Fall nicht viel bringt, wenn sie nicht auch sinnvoll behandelt wird. Was bringt mir eine ungefangene Exception und Datenverlust gegenüber einfach nur Datenverlust?



  • Hm.
    Verstehe ich jetzt nicht ganz wieso du das als Problem siehst.

    Ich bin schon der Meinung dass es deutlich besser ist wenn ein Programm im Fehlerfall mit einer Fehlermeldung der CRT bzw. des OS abbricht, als wenn es im Fehlerfall "OK, haben fertig, alles gut" hinschreibt.
    Du nicht?
    Ich finde falsche Erfolgsmeldungen halt schon sehr schlimm.

    Natürlich ist es meistens besser die Exception irgendwo zu fangen. Nur wie und wo man Exceptions sinnvoll behandelt würde hier mMn. deutlich den Rahmen sprengen. Und einfach nur ein try-catch um die ganze Show drumrumschreiben würde das Beispiel wohl kaum signifikant verbessern. Da das im Falle des Falles dann auch 1:1 mitkopiert wird, auch wenn es in dem Programm wo es reinkopiert wird an der Stelle wo es dann landet total deplaziert ist.

    Mir ging es lediglich darum eine bessere Alternative zu " close einfach weglassen" aufzuzeigen. Vor allem da man leider so oft Tips wie " close brauchst du nicht" lesen muss. (Was für ifstream ja OK ist, aber halt nicht für ofstream .)

    ps:
    Wäre es deiner Meinung nach besser in solchen Beispielen etwas ala

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if 
    if (!file)
    {
        // TODO: Fehler behandeln
    }
    

    zu schreiben?
    Oder hast du nen anderen, besseren Vorschlag?
    Aufruf einer im Beispiel nicht definierten Funktion mit beschreibendem Namen ala DateiSchreibeFehlerBehandeln() vielleicht? Damit der Code nach 1:1 copy+paste wenigstens nicht compiliert?


  • Mod

    Ich hätte es halt besser gefunden, wenn du ein bisschen auf mögliche Reaktionen auf den möglichen Fehler eingegangen wärst, denn

    }
    

    = schlecht

    und

    file.close(); 
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");	
    }
    

    = besser

    trifft es nicht wirklich. Da fehlt zu viel Drumherum, um dem Niveau der Frage gerecht zu werden.



  • SeppJ schrieb:

    Da fehlt zu viel Drumherum, um dem Niveau der Frage gerecht zu werden.

    Welches Drumherum, das direkt mit der Frage des OP in Zusammenhang steht, geht dir ab?

    Ich finde es auch einigermassen anstrengend dass du nicht sagst was dir eigentlich nicht passt/abgeht/.... Und zu guter Letzt: wenn etwas wichtiges fehlt, dann ergänze es doch einfach.


  • Mod

    Ich kann nicht sagen, was da fehlt, ist schließlich deine Antwort. Du antwortest auf dem Niveau professioneller Entwicklung, wo Datenkonsistenz sicher gestellt werden muss, erklärst aber nur, wie du einen eventuellen Datenverlust überhaupt feststellst. Der Fragesteller will hingegen vermutlich eher wissen, ob seine Datei überhaupt zu geht. Deine Antwort sieht so aus, als solle der Fragesteller den Code

    file.close();
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    in seinen Anfängerprogrammen übernehmen und sie würden dadurch automatisch irgendwie besser dadurch, dass er nun eine ungefangene Exception erhält, wenn ihm irgendjemand den Datenträger abzieht, während er "Hello World" in eine Datei schreiben möchte.



  • SeppJ schrieb:

    Ich kann nicht sagen, was da fehlt, ist schließlich deine Antwort. Du antwortest auf dem Niveau professioneller Entwicklung, wo Datenkonsistenz sicher gestellt werden muss, erklärst aber nur, wie du einen eventuellen Datenverlust überhaupt feststellst. Der Fragesteller will hingegen vermutlich eher wissen, ob seine Datei überhaupt zu geht. Deine Antwort sieht so aus, als solle der Fragesteller den Code

    file.close();
      if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    in seinen Anfängerprogrammen übernehmen und sie würden dadurch automatisch irgendwie besser dadurch, dass er nun eine ungefangene Exception erhält, wenn ihm irgendjemand den Datenträger abzieht, während er "Hello World" in eine Datei schreiben möchte.

    Sorry Sepp, du vermutest zu viel.
    Die Erklärung von hustbaer fand ich jetzt nicht schlecht. Es geht mir nicht darum in Erfahrung zu bringen ob meine Datei geschlossen wird, sondern wie im Anfangsthread schon geschreiben, wann ein .close sinnvoll ist oder eben nicht usw usw.

    Ich meine eher, dass das reine Werfen der Exception in diesem Fall nicht viel bringt, wenn sie nicht auch sinnvoll behandelt wird. Was bringt mir eine ungefangene Exception und Datenverlust gegenüber einfach nur Datenverlust?

    Das jmd. mitbekommt das jetzt was schiefgegangen ist ?



  • hustbaer schrieb:

    Hab selbst grad noch ein wenig nachgelesen/rumprobiert.
    Noch besser wäre gleich close aufzurufen und danach den Stream-State zu prüfen.
    Also

    file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if
    if (!file)
        throw std::runtime_error("Waaaaaaaah, MIST!");
    

    close setzt nämlich im Falle des Falles auch das fail Bit, und das Prüfen des Stream-States scheint auch nach dem Schliessen des Files erlaubt zu sein.
    => Besser als die flush() Variante, da man keine Vermutung mehr darüber anstellen muss ob close nach erfolgreichem flush noch fehlschlagen kann/darf oder nicht.

    Jetzt hast du aber wieder alle Nachteile des manuellen Aufräumens. Stell dir mal vor, eine andere Exception (die später gefangen wird und der User vermutet nicht, dass die Datei fehlerhaft ist) wird geworfen. Dann ists vorbei mit deiner Aufräumaktion. Wenn also nichts anderes dagegenspricht, sollte man denke ich direkt die Exceptionmaske für den ofstream richtig setzen und auf close() verzichten.



  • m.e. schrieb:

    Stell dir mal vor, eine andere Exception (die später gefangen wird und der User vermutet nicht, dass die Datei fehlerhaft ist) wird geworfen.

    Diesen Satz verstehe ich nicht. Kannst du das in (Pseude-)Code skizzieren?

    m.e. schrieb:

    Wenn also nichts anderes dagegenspricht, sollte man denke ich direkt die Exceptionmaske für den ofstream richtig setzen und auf close() verzichten.

    Soll mir auch Recht sein.
    Mir ist hauptsächlich wichtig dass die Leute wissen warum es ein Fehler ist einfach nix zu machen.



  • hustbaer schrieb:

    m.e. schrieb:

    Stell dir mal vor, eine andere Exception (die später gefangen wird und der User vermutet nicht, dass die Datei fehlerhaft ist) wird geworfen.

    Diesen Satz verstehe ich nicht. Kannst du das in (Pseude-)Code skizzieren?

    try
    {
    	ofstream file("simulation_results.txt");
    
    	for(int i = 0; i < 10; ++i)
    	{
    	    file << runSimulation(i);
    	    notifyUser("Simulation " + to_string(i) + " abgeschlossen. Ergebnisse geschrieben.");
    	}
    	file.close(); // liefert nicht den Stream zurück, geht daher nicht direkt im if
    	if (!file)
    	    throw std::runtime_error("Waaaaaaaah, MIST!");
    }
    catch(SimulationFailedException&)
    {
    	notifyUser("Eine Simulation ist gescheitert. Alle vorherigen wurden gespeichert.");  // Hier lügen wir den User an.
    }
    

    Natürlich könnte man das Problem auf einfache Weise beheben. Aber mit gesetzen Ostream-Exceptions würde es erst gar nicht zum Problem kommen.



  • Natürlich könnte man das Problem auf einfache Weise beheben. Aber mit gesetzen Ostream-Exceptions würde es erst gar nicht zum Problem kommen.

    Könntest du das bitte auch kurz darlegen ? Die "einfache" Weise, udn wie man die Ostream-Exceptions "einschaltet" ... Ich find das irgendwie nicht



  • @m.e.
    OK, danke, verstehe.
    Ja, es gibt Leute die auf die Idee kommen könnten solchen Code zu schreiben. Der einfachste "Fix" ist dabei aber mMn. keinen "komischen" Code zu schreiben 😃

    @beg_offl
    http://en.cppreference.com/w/cpp/io/basic_ios/exceptions

    myStream.exceptions(std::ios::failbit | std::ios::badbit);
    


  • m.e. schrieb:

    Wenn also nichts anderes dagegenspricht, sollte man denke ich direkt die Exceptionmaske für den ofstream richtig setzen und auf close() verzichten.

    Der Destruktor wirft nie irgendwas (und wenn, dann hast du ein). Das close() musst du deshalb immer noch hinschreiben.



  • * und wenn, dann hast du ein Problem.


Anmelden zum Antworten