C++ Datei schließen ?



  • 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.



  • throwingdestructor schrieb:

    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.

    Uff, ja, good catch!
    Hab ich einfach drübergelesen.
    Ja, der iostreams Dtor wirft, eben wie ein braver Dtor, niemals Exceptions!

    BTW: close() ist auch "brav". Falls der implizite Flush in close() schief geht, und die Exception-Maske verlangt dass eine Exception geworfen wird, dann tut close() trotzdem vor dem Werfen der Exception noch das Handle schliessen. Ist zwar ein etwas "eigenwilliges" Verhalten, aber wenn man drüber nachdenkt ... die Alternative das Handle in dem Fall nicht zu schliessen wäre viel schlimmer.



  • Also grob zusammenfassend :

    exceptions an , manuelles close, stream state prüfen und darauf reagieren....

    => so sollte man relativ auf der sicheren Seite sein, ja ?



  • Also jede Ressource am Ende ihrer Existenz closen wie damals in C? WTF! Da muss ich was übersehen haben.



  • volkard schrieb:

    Also jede Ressource am Ende ihrer Existenz closen wie damals in C? WTF! Da muss ich was übersehen haben.

    Nein, nur welche die bei close etwas machen was schief gehen kann. Wie Buffers zu flushen.
    Jetzt stell dich mal nicht blöder als blurry ist 🤡



  • beg_offl schrieb:

    Also grob zusammenfassend :

    exceptions an , manuelles close, stream state prüfen und darauf reagieren....

    => so sollte man relativ auf der sicheren Seite sein, ja ?

    Wenn du die Exceptions an hast, dann brauchst du den Stream-State nicht nach close nochmal prüfen. Das ist dann wirklich redundant.
    Eins von beiden reicht völlig.



  • hustbaer schrieb:

    volkard schrieb:

    Also jede Ressource am Ende ihrer Existenz closen wie damals in C? WTF! Da muss ich was übersehen haben.

    Nein, nur welche die bei close etwas machen was schief gehen kann. Wie Buffers zu flushen.

    Also wie in Java, wo man immer wissen musste, ob die Sachen NUR Ram oder so billige Sachen brauchen, also der GC genommen werden darf, oder ob sie mehr brauchen und man selber Hand anlegen muss.
    Das nun in C++, ich muss zu jeder Klasse erst schnell in die Doku schauen, ob sie brav ist, oder ob was schiefgehen kann. Und wenn ich mir offenlassen will, wie spätere Kollegen die Klassen evtl ausbauen, sollte ich außer bei einfachen Fällen wie vector<int> lieber davon ausgehen, daß was schiefgehen kann.

    hustbaer schrieb:

    Jetzt stell dich mal nicht blöder als blurry ist 🤡

    Du mach bitte die Augen für das Problem auf, das Du da verharmlost.



  • @volkard
    Ich verstehe nicht ganz was du willst.

    (Mit "du" meine ich im Folgenden nicht dich persönlich - bloss einfacher zu schreiben als mit "jemand" oder "man".)

    Also...
    Es gibt da eine Klasse. Die tut buffern bevor sie die Daten rausschreibt. Und zwar auf einem Weg wo was schief gehen kann (Disk, Netzwerk, ...).
    Das ist dir alles bekannt.
    Dir ist auch bekannt dass es in C++ super-verpönt ist aus einem Dtor Exceptions zu werfen. Und dass man daher davon ausgehen kann (bzw. in diesem Fall: muss) dass Klassen das auch nicht tun.

    Von der Klasse erzeugst du dann ein Objekt, steckst Daten rein, und zerstörst dann das Objekt. Ohne vorher irgendwie close oder flush o.Ä. aufzurufen. Und meldest dann "Erfolg!".

    Jetzt komme ich, und sage dir dass du da eventuell ein Problem hast, weil dein Programm nirgends sicherstellt, dass die Daten auch wirklich rausgeschrieben worden sind, bevor du "Erfolg!" meldest.

    Wenn du überrascht bist, dann hast du nicht mitgedacht.

    Klar, es ist ein Problem. Bloss kein besonders kniffliges oder gar unlösbares.
    Man muss sich dessen bloss bewusst sein.

    Ist halt grundsätzlich ein Problem wenn man etwas macht was das Auftreten von möglichen Fehlern auf einen späteren Zeitpunkt verlagert. Nämlich den Zeitpunkt wo man "mach jetzt!" sagt. Und wenn man beschliesst auf eine Art und Weise "mach jetzt!" zu sagen, die dafür sorgt dass ein eventueller Fehler nicht reportet wird (in diesem Fall: über den Dtor, der keine Exception werfen darf), dann muss einem auch klar sein dass auftretende Fehler halt verschluckt werden.

    Aber sehen wir uns mal die möglichen Alternativen an.

    1: Kein Buffering. Scheidet mMn. für die allermeisten Anwendungen aus, da viel zu langsam.

    2: Man erlaubt dem Dtor Exceptions zu werfen. Das ist grundsätzlich möglich, aber in C++ ... nicht gut. Wegen der Sache dass eine Exception während des Unwindings zum sofortigen Abbruch führt. In Sprachen wie C# ist das anders gelöst, da gewinnt bei einer Exception während des Unwindings einfach eine der beiden (hab jetzt nicht im Kopf ob die ältere oder die neuere, ist hier aber auch egal). Hätte C++ ähnliche Regeln, dann wäre das eine sinnvolle Option, aber so wie es Anno 2015 ist ... halt leider nicht.

    3: Man versucht über Assertions sicherzustellen dass der Programmierer keinen Fehler macht. Das wäre mMn. die beste Variante. Im Fall ofstream müsste der Dtor dann ein assert(!is_open() || flush_was_called_after_the_last_write()) o.Ä. enthalten.

    Ansonsten wüsste ich nicht wie man die Situation verbessern könnte.

    ----

    ps: Falls es das ist was dir sorgen bereitet: Das OS File Handle das vom ofstream kontrolliert wird, wird in jedem Fall korrekt freigegeben. Auch z.B. wenn man die Exceptions eingeschaltet hat, und close() dann wegen "kann nicht flushen" eine Exception wirft. close() schliesst dann vor dem Werfen der Exception trotzdem den Stream-Buffer so dass das OS Handle freigegeben wird. Ebenso sorgt der Dtor dafür dass in jedem Fall das OS Handle freigegeben wird.



  • hustbaer schrieb:

    @volkard
    Ich verstehe nicht ganz was du willst.

    (Mit "du" meine ich im Folgenden nicht dich persönlich - bloss einfacher zu schreiben als mit "jemand" oder "man".)

    Das ist mir zu unhöflich. Und "du" statt "man" bin ich nicht so sehr gewohnt, da ich keine Nachmittagstalkshows schaue.

    (Mit "du" meine ich im Folgenden nicht dich persönlich - bloss einfacher zu schreiben als mit "jemand" oder "man".)
    Du bist doof. Uih, war das einfach zu schreiben.

    Also muss man FAST JEDES Objekt per Hand closen, weil man im Allgemeinen nicht wissen will, ob es noch verzögerte Aktionen hat. Was ist an diesem Problem so unsichtbar?

    //vermutlich auch nicht die generelle lösung. 
       map<Foo,ofstream> ma;
       …
       try{//danke, husti
          for(auto i:ma){//danke, husti
             i.first()->terminateGracefully();//danke, husti
             i.second().flush();//danke, husti
          }//danke, husti
       }catch(...}{//danke, husti
          clog<<"something went wrong\n";//danke, husti
          clog<<"please contact your administrator\n"<<flush;//danke, husti
          throw;//danke, husti
       }//danke, husti
       //außerdem javaesk, kann wohl kaum gut sein.
    }
    

Anmelden zum Antworten