In Datei schreiben in einer Unterfunktion



  • Hi,

    Ich habe ein Programm, das eine Textdatei ausliest, die Daten in einer Struktur speichert und mit den Daten drei verschiedene Berechnungen durchführt. Die drei Berechnungen habe ich in 3 Unterfunktionen ausgegliedert. Jede Berechnung hat 2 Matrizen als Ergebnis. Zur Ausgabe übergebe ich diese Matrizen jeweils an eine Ausgabefunktion innerhalb der Unterfunktionen. Soweit klappt alles.

    Jetzt möchte ich aber statt einer Ausgabe die Ergebnisse in einer Textdatei haben. Normalerweise mache ich das so:

    ofstream output;
    output.open("output.txt");
    output << "bla";
    

    Wenn ich das aber in der gleiche Unterfunktion wie die Ausgabe mache, wird bei jedem Aufruf der bisherige Text in der Textdatei überschrieben.
    Wie sollte man da vorgehen? Soll ich die Datei schon in der main Funktion öffnen und irgendwie als Parameter durchreichen bis zur Unterfunktion der Unterfunktion? Wenn ja wie? Oder gibt es da andere Methoden? Oder sollte ich den gesamten Programmaufbau überdenken? Mir fällt es schwer zu erkennen welche Methode am wenigsten Rechenaufwand bedeutet.

    Ich hoffe ihr könnt mir weiterhelfen. Vielen Dank im Voraus.


  • Mod

    Die übliche Lösung ist, den Stream durchzureichen. Dies ist auch normalerweise kein großer Aufwand, denn der Abstand von der Stelle, die die Datei verwaltet zu der Stelle, an der tatsächlich etwas geschrieben wird, sollte nicht groß sein. In der Regel habe ich bei mir bloß 1-2 Funktionsaufrufsebenen dazwischen - meistens ruft die Stelle mit der Datei die Ausgabefunktion auf. Falls dies nicht so ist, solltest du den Aufbau deines Programms noch einmal überdenken, Stichwort wäre hier - unter anderem - Separation of concerns. Eine Rechenfunktion sollte nichts ausgeben, sondern ihr Ergebnis zurück geben. Dann braucht sie auch nichts über Dateien zu wissen. Darum kann sich die Stelle kümmern, die die Ergebnisse sammelt und darstellt.

    Das Ergebnis könnte dann ungefähr so aussehen (semi-Pseudocode):

    Matrix matrix1 = berechne_matrix1();
    Matrix matrix2 = berechne_matrix2();
    Matrix matrix3 = berechne_matrix3();
    
    ofstream outfile("Foo.bar");
    outfile << matrix1 << matrix2 << matrix3;
    

    Dabei ist die Schreibweise mit dem überladenen und verketteten Operator<< auch nur eine Schreibweise mit Extra-Syntaxzucker für eine Ausgabefunktion. Da steht im Prinzip nichts anderes als:

    Matrix matrix1 = berechne_matrix1();
    Matrix matrix2 = berechne_matrix2();
    Matrix matrix3 = berechne_matrix3();
    
    ofstream outfile("Foo.bar");
    write_matrix(outfile, matrix1);
    write_matrix(outfile, matrix2);
    write_matrix(outfile, matrix3);
    


  • Ok, danke schon mal.
    Wie schreibt man das? So irgendwie?

    main()
    {
       ofstream output ("output.txt");
       ausgabe(matrix, output);
    }
    
    ausgabe(vector<vector<int>> matrix, ofstream output)
    {
       output << "bla";
    }
    


  • Ok, so funktioniert das:

    main()
    {
       ofstream output ("output.txt");
       ausgabe(matrix, output);
    }
    
    ausgabe(vector<vector<int>> matrix, ofstream& output)
    {
       output << "bla";
    }
    


  • @TerenceNr1

    Jede Berechnung hat 2 Matrizen als Ergebnis.
    ...
    Mir fällt es schwer zu erkennen welche Methode am wenigsten Rechenaufwand bedeutet.

    Du solltest die Entscheidung wie du das implementierst nicht unbedingt davon abhängig machen was am wenigsten Rechenaufwand bedeutet.
    Weil es bei vielen Dinen Wurst ist wenn man 100, 200 oder sogar 1000% Overhead hat. Weil 1000% von einer Millisekunde immer noch bloss 10 Millisekunden sind. Und wenn man eine Sache nicht zig oder hunderte mal Pro Sekunde machen muss, ist es meist vollkommen Wurst ob sie 1 oder 11 Millisekunden dauert.
    Wäre das nicht so, gäbe es kein einziges Python Programm.

    Weiters kann man in C++ oft saubere Lösungen bauen, die auch prozentuell nur einen sehr geringen Overhead haben. Also doppelt Wurst.
    (Und ich gehe davon aus dass deine Matrix-Berechnungen so ein Fall sind.)

    => Mach es so, wie es am saubersten ist.
    Und das wäre mMn. der von SeppJ vorgeschlagene Code.


  • Mod

    Obligatorischer Hinweis, dass ein vector<vector> sich zu einer Matrix verhaelt, wie

    ###
    ######
    ####
    ########
    #####
    

    sich zu

    #####
    #####
    #####
    #####
    

    verhaelt.



  • Obligatorischer Hinweis, dass man einen vector an eine reine Ausgabefunktion nicht by-value übergeben sollte, sondern als const-ref:

    void ausgabe(vector<vector<int>> const& matrix, ofstream& output) 
    { 
        output << "bla"; 
    }
    

    Weiterer Hinweis, dass man keine Referenz auf ofstream als Parameter nehmen sollte, wenn man auch mit einem ostream auskommt (was in deinem Programm vermutlich der Fall sein wird), also:

    void ausgabe(vector<vector<int>> const& matrix, ostream& output) 
    { 
        output << "bla"; 
    }
    


  • hustbaer schrieb:

    Obligatorischer Hinweis, dass man einen vector an eine reine Ausgabefunktion nicht by-value übergeben sollte, sondern als const-ref:

    kommt drauf an, was das profiling sagt. Wenn ein Programm 0.1% der Laufzeit in der Ausgabefunktion by-value verbringt, kann man sich const & wohl sparen.



  • Dankeschön,
    Ich habe jetzt die Ausgabefunktion in der main und übergebe ostream&. Die Berechnungsfunktionen geben die jeweils notwendige Vektoren-Matrix zurück, die dann von der Ausgabefunktion verwendet wird.
    Das mit der Länge des vector<vector<int>> habe ich im Griff. Sobald die notwendige Größe eingelesen wurde, setze ich alle Vektoren auf eine feste Länge.



  • großbuchstaben schrieb:

    hustbaer schrieb:

    Obligatorischer Hinweis, dass man einen vector an eine reine Ausgabefunktion nicht by-value übergeben sollte, sondern als const-ref:

    kommt drauf an, was das profiling sagt. Wenn ein Programm 0.1% der Laufzeit in der Ausgabefunktion by-value verbringt, kann man sich const & wohl sparen.

    const& hinten dranzuschreiben ist kein Aufwand und hat - wenn man eh nur Lesen will - keine Nachteile.
    Wieso es sich dann "sparen"? Was soll das bringen? 6 Buchstaben weniger tippen müssen?

    Bei sowas spart man Zeit indem man sich angewöhnt es immer und überall "richtig" zu machen. Geht viel schneller als sich 100x am Tag zu überlegen was der Profiler wohl dazu sagen könnte. Und dann noch die Stellen nachträglich suchen zu dürfen wo man falsch geraten hat.



  • großbuchstaben schrieb:

    hustbaer schrieb:

    Obligatorischer Hinweis, dass man einen vector an eine reine Ausgabefunktion nicht by-value übergeben sollte, sondern als const-ref:

    kommt drauf an, was das profiling sagt. Wenn ein Programm 0.1% der Laufzeit in der Ausgabefunktion by-value verbringt, kann man sich const & wohl sparen.

    C++ Coding Standards: Premature pessimization.
    Es schadet auf keinen Fall und ist nahezu kein Mehraufwand. Exakt dasselbe mit i++ und ++i.


  • Mod

    TerenceNr1 schrieb:

    Das mit der Länge des vector<vector<int>> habe ich im Griff. Sobald die notwendige Größe eingelesen wurde, setze ich alle Vektoren auf eine feste Länge.

    Du bezahlst aber trotzdem die Kosten, alleine für die Möglichkeit, dass der vector<vector> auch flexibler sein kann.



  • hustbaer schrieb:

    großbuchstaben schrieb:

    hustbaer schrieb:

    Obligatorischer Hinweis, dass man einen vector an eine reine Ausgabefunktion nicht by-value übergeben sollte, sondern als const-ref:

    kommt drauf an, was das profiling sagt. Wenn ein Programm 0.1% der Laufzeit in der Ausgabefunktion by-value verbringt, kann man sich const & wohl sparen.

    const& hinten dranzuschreiben ist kein Aufwand und hat - wenn man eh nur Lesen will - keine Nachteile.
    Wieso es sich dann "sparen"? Was soll das bringen? 6 Buchstaben weniger tippen müssen?

    nein, das const sollte aus systematischen Gründen natürlich bleiben. Es geht um das "&".

    Ich kleistere meinen handgepflegten Code nicht aus Jux und Tollerei mit Sonderzeichen zu. Ich ersetze pass-by-value für gewöhnlich erst dann, wenn es einen Grund gibt, und den liefert mir das profiling (falls es nicht ohnehin offensichtlich sein sollte, wie etwa bei 3-mal aufgerufenen Funktionen mit 20 Bytes Argument)



  • großbuchstaben schrieb:

    nein, das const sollte aus systematischen Gründen natürlich bleiben.

    Dem stehe ich im Moment zwiegespalten gegenüber. Momentan mach ich's nicht, weil's mir irgendwie komisch vorkommt. Allerdings conste ich so-gut-wie alle lokalen Variablen die sich nimmer ändern werden. Und Parameter sind ja auch nix grundlegend anderes als lokale Variablen. Also wieso nicht die auch consten?
    Hmmm...
    Egal. Darum ging's mir nicht.

    großbuchstaben schrieb:

    Es geht um das "&".

    Genau 🙂

    großbuchstaben schrieb:

    Ich kleistere meinen handgepflegten Code nicht aus Jux und Tollerei mit Sonderzeichen zu.

    Ist ja nicht aus Jux und Tollerei.

    Dinge wie new/delete oder CAS sind in der Regel nicht ganz billig (verglichen mit dem Auf-Den-Stack-Pushen eines Zeigers), und können in der Regel vom Compiler auch nicht "as if" wegoptimiert werden. Weil er in der Regel nicht beweisen kann dass sie nicht beobachtbar sind.

    großbuchstaben schrieb:

    Ich ersetze pass-by-value für gewöhnlich erst dann, wenn es einen Grund gibt, und den liefert mir das profiling (falls es nicht ohnehin offensichtlich sein sollte, wie etwa bei 3-mal aufgerufenen Funktionen mit 20 Bytes Argument)

    OK.
    Ich bin mir nicht sicher wie gut man Slowdowns die durch Verzicht auf pass-by-const-ref entstehen mit nem Profiler erkennen kann. Ich hab' da meine Zweifel.
    Ich müsste es wohl mal ausprobieren.

    Nur: ich sehe einfach keinen Grund der dagegen spricht.
    Das mit den Sonderzeichen kann ja wohl (hoffentlich) nur ein Scherz gewesen sein. Ist für mich auf jeden Fall kein akzeptabler Grund.



  • hustbaer3e schrieb:

    Und Parameter sind ja auch nix grundlegend anderes als lokale Variablen. Also wieso nicht die auch consten?

    Im Interface bringts nichts, sondern ist unnötige Information für den Aufrufer. Natürlich könnte man Deklaration und Definition verschieden schreiben, aber das führt auch wieder zu Fragen. Top-Level- const für Parameter oder Rückgabetypen finde ich echt mühsam. Ist bei vielen Codes ein Hinweis, dass der Autor diverse C++-Konzepte nicht verstanden hat, besonders wenn die Antwort auf die Frage "warum" dies bestätigt... War zumindest meine Erfahrung.

    Davon abgesehen drückt man mit dem Parametertyp auch Semantik, besonders im Bezug auf Besitz, aus. const T& bedeutet in meinem Code "Funktion liest lediglich Werte aus", während T mittlerweile nicht selten auf ein Move hinweist. Ist kein 100% verlässlicher Indikator, aber hat bei mir dazu geführt, dass ich const Klassentyp& sofort als nicht speziell wahrnehme, während ich bei Pass-by-Value jeweils kurz überlege, warum es jetzt so angegeben wurde.

    Schlussendlich ist das recht subjektiv, aber wenn man sich an gewisse semantische Regeln hält, kann bereits wenig Code sehr viel aussagen. Hab ich auch gemerkt, als ich in letzter Zeit wieder vermehrt in Java entwickeln musste, wo wir anhand der Signatur rein gar nichts erkennen.



  • Geh Nexus, dass das top-level const bei Parametern nicht zur Signatur gehört, und daher in bei der Deklaration nix verloren hat (weil sinn- und wirkungslos), das weiss ich doch.
    (top-level const beim Returnwert gehört dagegen natürlich zur Signatur - ist aber mMn. aus anderen Gründen plem)

    Mir geht's schon um die Definition.
    Weil dort dadurch dokumentiert wird "keine sorge, ich änder das nicht, mussdu nicht die ganze Funktion lesen sondern darfst du sicher sein".
    Weswegen ich es bei lokalen Variablen überall drankleistere.

    void foo(int /*wieso hier kein const*/ param)
    {
        int const /* aber hier schon */ bar = compute_bar(param);
    }
    

    Und da hab ich einfach keinen *guten* Grund dafür. Mein schlechter Grund ist: weil ich es komisch finde. Unpassend. Ungewohnt.
    Nur vor etlichen Jahren fand ich das const bei "bar" auch komisch, unpassend, ungewohnt. Und heute schreib' ich es ohne nachzudenken hin. Genau so wie explicit bei Konstruktoren und hoffentlich bald sealed bei C++/CLI ref bzw. C# Klassen - wenn ich es endlich schaffe mir das anzugewöhnen.



  • mir ist es wichtig, schnell im Code navigieren zu können und dazu muß relevante Information möglichst deutlich hervorgehoben sein, und irrelevante Information möglichst unterdrückt. Ziel ist selbsterklärender Code.

    "const" ist für mich eine wichtige Information, um bspw in der Deklaration auf einen Blick zu erkennen, ob ein Parameter ein Rückgabewert ist oder nicht. Oder ob eine Methode das Objekt ändert.

    "&" ohne "const" ist für mich keine sonderlich relevante Information; es sagt mir vor allem, daß das entsprechende Argument so groß werden könnte, daß pass-by-value ineffizient werden könnte, was mit einem Profiler zu überprüfen wäre.

    "const&" wiederum ist eine relevante Information für mich, sagt es mir doch, daß es sich beim zugehörigen Parameter 1. nicht um einen Rückgabewert handelt und 2. daß pass-by-value hier einen Unterschied machen könnte / die Funktion bereits optimiert ist.



  • oops, jetzt habe ich's genau verkehrt herum geschrieben 👎

    Gemeint ist:

    "const&" und "const" ist für mich erst bei Effizienzfragen von Relevanz, "&" alleine hingegen von Anfang an relevant (Rückgabewert).


Log in to reply