C++ Datei schließen ?



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


  • LOL, ein paar lustige Sachen kann man in diesem thread entdecken.



  • hustbaer schrieb:

    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.

    (Mit "der" meine ich im Folgenden "die" - bloss einfacher zu schreiben.)
    Vor ca 25 Jahren hatten meine Containerklassen eine öffentliche clear() und im Destruktor ein assert(!isEmpty()). Aber irgendwie bin ich wieder davon abgekommen. Im aufrufenden Code musste ich ja alle exceptions selber behandeln, um das clear sicher aufzurufen. Der Idee war nicht tragfähig.
    Was soll ich machen, wenn zwei der tausend Objekte in ihrem close() versagen? Nicht beim ersten abbrechen, sondern wenigstens allen 1000 die Chance geben (probabilistischen Programmieren)? Der anfallenden Exceptions sammeln, in einen vector stopfen und werfen?
    Nur wie verarbeitet man der dann? Nicht mit den normalen Mechanismen, der dafür vorgesehen sind. Also besser, wir nehmen wieder int als errorcode.



  • volkard schrieb:

    (Mit "der" meine ich im Folgenden "die" - bloss einfacher zu schreiben.)

    Und damit war es jetzt auch genug.

    Ja, war blöd formuliert, das gebe ich zu. Was ich meinte: Ich wollte dir nicht unterstellen dass DU wirklich überrascht wärst in dem beschriebenen Fall. Da ich aber sowieso "Wenn du überrascht bist,..." geschrieben habe, war die Vorab-Erklärung eigentlich total unnötig.

    volkard schrieb:

    Das ist mir zu unhöflich.

    Gut. Zur Kenntnis genommen.

    Und die Art und Weise wie du damit umgehst ist mir zu unhöflich. Weil direkt beleidigend.

    Falls dich die sachliche Diskussion weiterhin interessiert, dann bring bitte ein konkretes Beispiel wo es deiner Meinung nach zu einem Problem kommt. Also mit Code/Pseudocode oder wenigstens einer genauen Beschreibung des Ablaufs und der beteiligten Klassen.
    Da ich hier eben gerade kein besonders schwieriges Problem sehe, hab ich natürlich auch keine Ahnung was du mit deinen sehr allgemeinen Formulierungen meinst.

    Vor ca 25 Jahren hatten meine Containerklassen eine öffentliche clear() und im Destruktor ein assert(!isEmpty()). Aber irgendwie bin ich wieder davon abgekommen. Im aufrufenden Code musste ich ja alle exceptions selber behandeln, um das clear sicher aufzurufen. Der Idee war nicht tragfähig.

    Liesse sich mit uncaught_exceptions (C++17) lösen.
    Damit könnte man in Dtor dann unterscheiden ob ein Objekt "normal" oder wegen Unwinding zerstört wird.
    Also dann eben ein assert(clean || unwinding) .

    Und was mir auch nicht ganz klar ist, ist worauf du eigentlich hinaus willst. EDIT: Aber gut, das steht oben schon (dass mir nicht ganz klar ist welches allgemeine, schwerwiegende Problem du hier siehst). /EDIT

    EDIT: Grammatik- und Flüchtigkeitsfehler korrigiert


Anmelden zum Antworten