"kopieren" von Streams



  • Hallo,

    ich lese gerade "Standard C++ IOStreams and Locales" von Angelika Langer und Klaus Kreft, und da bin ich auf was gestoßen, was mir mal bitte auf Deutsch erklärt werden muss.

    Folgender Codeausschnitt:

    ostringstream o2, o1;
    streamcpy(o1 /*dest*/, o2 /*source*/);
    o1 << "a";
    o2 << "b";
    cout << o1.str(); // gibt nix
    cout << o2.str(); //gibt "ab"
    

    Bei streamcpy handelt es sich lediglich um eine Funktion, wo mit rdbuf() der Stream Buffer vertauscht wird, mit clear() und rdstate() der iostate kopiert wird und mit copyfmt werden die fmtflags und so weiter kopiert.

    Warum gibt o1 nix aus, und o2 schon? Ich dachte, es müsste genau umgekehrt sein, oder identisch!? Schließlich ist der Stream Buffer von o2 nun auf o1. Was genau hab ich da nicht verstanden?

    Im Buch steht, der Stream Buffer von o1 wird ignoriert, warum das so ist, steht da nicht.


  • Mod

    Zeig mal streamcpy.



  • template<typename Stream>
    void copy_stream(Stream& dest, const Stream& source){
        dest.exceptions(std::ios::goodbit); // damits wirklich nicht knallt
        dest.template basic_ios<typename Stream::char_type, typename Stream::traits_type>::rdbuf(source.rdbuf());
        dest.clear(source.rdstate());
        dest.copyfmt(source);
    }
    

    Ich weiß, das kopiert den Stream nicht wirklich, aber eigentlich müssten doch nach diesem Aufruf beide ostringstream's den gleichen Stream Buffer halten, nicht? Warum also gibt mir o1.str() das blanke Nichts?


  • Mod

    Verzwickt. Erst einmal eine kleine Klarstellung: Vertauscht wird da nichts. Da wird nur einseitig etwas geändert.

    Die Verwirrung kommt hier (vollkommen zurecht) durch die multiplen Puffer in der Streamarchitektur. Ein Stringstream hat sowohl seinen internen Puffer, durch die Tatsache, dass er ein Stringstream ist und daher einen String enthält; als auch einen assoziierten Puffer, dadurch dass er ein Stream ist und irgendwie mit einem Pufferobjekt interagieren soll. In der Regel sind das beides die gleichen Objekte, aber sie werden von der internen Logik separat behandelt.

    Durch den Ausdruck

    dest.template basic_ios<typename Stream::char_type, typename Stream::traits_type>::rdbuf(source.rdbuf());
    

    wird der assoziierte Streambuf gesetzt, und zwar auf den internen Streambuf des anderen Streams. Von nun an gehen alle Operationen, die der Stringstream von den allgemeinen Streams geerbt hat, auf diesen Puffer.

    Die str-Methode ist aber etwas, das nur Stringstreams haben. Die geht auf den Teil des Stringstreams, die ihn zum Stringstream macht (im Vergleich zu allgemeinen Streams). Sprich: Sie spricht seinen internen Stringpuffer an.

    Die Streamschreibeoperationen gehen hier also beide auf den internen Puffer von o2. Aber die str-Aufrufe geben jeweils den Inhalt des internen Puffer des jeweiligen Strings zurück. Der ist bei o1 leer geblieben.



  • Erst einmal eine kleine Klarstellung: Vertauscht wird da nichts. Da wird nur einseitig etwas geändert.

    Ja, da hab ich mich wohl verplappert.

    Ein Stringstream hat sowohl seinen internen Puffer, durch die Tatsache, dass er ein Stringstream ist und daher einen String enthält; als auch einen assoziierten Puffer, dadurch dass er ein Stream ist und irgendwie mit einem Pufferobjekt interagieren soll. In der Regel sind das beides die gleichen Objekte, aber sie werden von der internen Logik separat behandelt.

    Ok, ich habe verstanden, dass ein stringstream also gleich zweimal einen basic_streambuf<> oder einer von ihr vererbten Klasse enthält (z.B. basic_stringbuf<> ). Einen auf den man zugreifen kann (den Assozierten), einen anderen, auf den man nicht zugreifen kann (den Internen). Also naja, doch, man kann den internen Buffer nach allen Schreiboperationen ändern:

    o1.str(o2.str());
    

    Aber man kann ihn nicht so umbiegen, dass der interne Buffer von o1 auf den internen Buffer von o2 zeigt. Das habe ich verstanden.

    Die Streamschreibeoperationen gehen hier also beide auf den internen Puffer von o2.

    Äääh, du meinst wohl auf den assozierten Buffer! Denn wenn str() den Inhalt des internen Buffers zurückgibt, und die Inserter-Operationen beide in den internen Buffer schreiben würden, so müsste die Ausgabe zweimal das Gleiche sein? Oder komm ich wieder durcheinander?

    Ich nenne mal den assozierten Buffer den normalen Buffer, der jeden Stream hat und den internen Buffer, den String-Buffer, der nur die stringstreams hat.

    Ich hoffe, ich konnte deinen Beitrag in allen Gänzen verstehen.


  • Mod

    Nein, das hast du noch nicht richtig verstanden. Ein Stringstream hat nur einen physikalischen Puffer. Das ist der String-Teil von ihm. Der Stream-Teil hat einen assoziierten Puffer. Das ist in der Regel der String-Teil des Stringstreams. Aber er kann auch separat auf etwas anderes gesetzt werden. Zum Beispiel auf den String-Teil eines anderen Stringstreams.


Anmelden zum Antworten