Temporären Binärstream schreiben/lesen



  • Hi,

    Ich möchte einen binären stream temporär schreiben und lesen.
    Das sollte:
    1. Nicht über ein echtes File gehen müssen (Erstellung im Filesystem) und
    2. Mit den streaming Operatoren << und >> funktionieren

    Also in etwa so:

    Foo f, f2;
    Bar b, b2;
    
    stream << f; // f schreibt binäre Daten
    strean << b; // b schreibt binäre Daten
    stream >> f2; // und wieder rein
    stream >> b2 //     --||--
    
    assert(f==f2);
    assert(b==b2);
    

    Das Problem ist, dass ich keine Implementierung eines solchesn Streams gefunden habe, der die Streaming Operatoren auch für binäre Objekte überlädt. Boost.Serialization schreibt halt noch mehr in den Stream hinein, was nicht erwünscht ist.

    Ich dachte, dass es so etwas relativ schnell ohne viel Aufwand möglich sein sollte, aber habe weder in der STL, boost.iostreams noch in boost.serialization etwas gscheites gefunden.

    Habe ich das etwas übersehen oder muss man da wirklich selbst einen passenden Stream bauen?



  • hola

    die schnellste moeglichkeit die mir einfaellt waere:

    class memstream : public std::strstream
    {
    
    };
    
    template<class T>
    memstream& operator<<(memstream &out,  const T &e)
    {
       out.write(reinterpret_cast<const char*>(&e), sizeof(e));
       return out;
    }
    
    template<class T>
    memstream& operator>>(memstream &in, T &e)
    {
       in.read(reinterpret_cast<char*>(&e), sizeof(e));
       return in;
    }
    
    ...
    
       int i = 12;
       short z = 110;
    
       memstream mem;
       mem << i << z;
       i = 0; z = 0;
       mem >> i >> z; 
       std::cout << i << " " << z << std::endl; // gibt 12 110 aus
    

    muesste mal werner oder sonst jemand von den kompetenten leuten seinen senf dazu geben ob das korrekter code ist. wird naemlich (in VC2012) warning 4250 rausgeworfen. die operatoren << und >> sind in dem fall natuelich nur fuer pods zulaessig.

    Meep Meep



  • Ja, so etwas ging mir schon auch durch den Kopf, aber eben ging es mir darum, dass ich das eben nicht selbst machen muss, sondern etwas bestehendes verwenden kann. Hätte eben gedacht, dass es da doch etwas geben muss.

    Auf jeden Fall schon mal Danke für die Antwort! Würde mich auch für die Antwort von Werner interessieren.



  • Wieso willst du dafür formatierte Streams benutzen?
    Und warum willst du operator<<() verwenden (der seine Vorzüge ja eher in der formatierten I/O hat), wenn du die Daten binär liest und schreibst (also unformatiert)?
    Scheinbar willst du die Daten ja nur kopieren.
    Unter der Annahme, dass du mit PODs arbeitest, benutze doch einfach std::memcpy(), andernfalls Kopierkonstruktoren, um ein Objekt zu kopieren oder zu konvertieren. Ein eigener Stream ist da total überflüssig.

    Edit: Meep Meeps Code drückt die Redundanz sehr schön aus: Er lässt einfach die formatierte Ausgabe unformatiert ablaufen. Dann kann man auch gleich write() benutzen. Oder std::memcpy(), da spart man sich das doppelte Kopieren Objekt --> Puffer --> Objekt.



  • Wer sagt, dass ich einen formatierten Stream benutzen möchte?

    Es geht darum Code zu testen, der die Streaming Operatoren dafür benutzt und jetzt brauche ich einen temporären binären Stream, wo ich die Objekte rein und wieder raus streamen kann. Ich habe schon meine Gründe, warum ich die Bedingungen oben hingeschrieben habe..



  • > Wer sagt, dass ich einen formatierten Stream benutzen möchte?

    Du, denn du willst operator <<() und operator >>() benutzen. Natürlich kann man auch unformatiert mit diesen Operatoren arbeiten, aber dafür wurden sie und das ganze Manipulator-Konzept nicht erfunden.
    Die ganze Geschichte kostet dich einfach nur eine Kopie mehr als ein gewöhnliches Copy. Wie auch immer, mit einigen Boost-Libraries kann man etwas vergleichbares ohne zusätzliche Kopien zaubern: http://stackoverflow.com/questions/2079912/simpler-way-to-create-a-c-memorystream-from-char-size-t-without-copying-t



  • drakon schrieb:

    Wer sagt, dass ich einen formatierten Stream benutzen möchte?

    Es geht darum Code zu testen, der die Streaming Operatoren dafür benutzt und jetzt brauche ich einen temporären binären Stream, wo ich die Objekte rein und wieder raus streamen kann. Ich habe schon meine Gründe, warum ich die Bedingungen oben hingeschrieben habe..

    Ein stringstream mit ios_base::bin tuts nicht (tuts bestimmt nicht, aber zur Sicherheit frage ich nach)?



  • ich glaub nicht das es bei stringstreams das binary flag gibt.
    was ich mir aber vorstellen koennte, ist das man eventuell ein eigenes facet erstellen koennte.



  • Meep Meep schrieb:

    ich glaub nicht das es bei stringstreams das binary flag gibt.

    Laut der C++-Referenz schon, und ich liefer auch hoffentlich gleich den Standard.



  • Sone schrieb:

    Meep Meep schrieb:

    ich glaub nicht das es bei stringstreams das binary flag gibt.

    Laut der C++-Referenz schon, und ich liefer auch hoffentlich gleich den Standard.

    Nope. Der Standard sagt dazu soweit ich sehen kann überhaupt nichts, also nehme ich zurecht an das es geht.

    Warten wir ab was Drakon dazu sagt.



  • hab mal getestet

    int i = 12;
       short z = 110;
       std::stringstream stream(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
       stream << i << z << "test";
    

    im speicher steht dann:

    12110test
    

    binary hat wohl nur den effekt das keine interne konvertierung statt findet.



  • Genau. Habe ich auch schon ausprobiert gehabt. Geht leider mit stringstream nicht.

    So wies aussieht könnte boost.serialization doch funktionieren, wenn man da den ganzen Overhead rausnehmen kann.

    Hatte eben noch im Kopf, dass es auch einfacher (mit reinen Standardmitteln) geht.
    Hat nicht mal sogar hier jemand so einen stream gebaut?



  • drakon schrieb:

    Hat nicht mal sogar hier jemand so einen stream gebaut?

    Gut möglich. Aber finde das erstmal wieder.



  • Ah. Habs gefunden. Der Beitrag ist mir noch irgendwie im Kopf rumgeschwebt:
    http://www.c-plusplus.net/forum/235776

    @Meep Meep: Dir hätte dein Code wohl doch auch irgendwie bekannt vorkommen sollen. ^^

    Oh, gerade gesehen, dass es anscheinend noch vor demjenigen Thread etwas gegeben hat. Naja, wie auch immer. Scheint ja so, dass man das selbst machen muss oder vielleicht bringt man ja boost.serialization dazu das zu machen.

    Danke auf jeden Fall für die Antworten!



  • also mit nem facet funktionierts. hab folgendes gemacht:

    struct binary_num_put : std::num_put<char> 
    {
       iter_type do_put(iter_type s, std::ios_base& f, char_type fill, long v) const 
       { 
          char *p = reinterpret_cast<char*>(&v);
          *s = p[0];
          ++s;
          *s = p[1];
          ++s;
          *s = p[2];
          ++s;
          *s = p[3];
          ++s;
          return s;
       } 
    
       iter_type do_put(iter_type s, std::ios_base& f, char_type fill, unsigned long v) const 
       { 
          char *p = reinterpret_cast<char*>(&v);
          *s = p[0];
          ++s;
          *s = p[1];
          ++s;
          *s = p[2];
          ++s;
          *s = p[3];
          ++s;
          return s;
       } 
    };
    
    int main(int param_count, const char **params)
    {
       long i = 12;
    
       std::stringstream stream(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
    
       stream.imbue(std::locale(std::locale(),new binary_num_put));
    
       stream <<  i << "test";
    
       std::ofstream datei("test.bin");
       datei.write(stream.str().c_str(), stream.str().size());
    
       return 0;
    }
    

    in der datei hab ich dann

    0C 00 00 00 74 65 73 74
    

    drinnen (mit nem hexeditor angesehen).



  • leider zu frueh gefreut.
    wie es aussieht gibt esin der tsd::num_put keine uberladung fuer z.b short.
    da wird dann die ueberladung fuer long aufgeruden. dadurch stimmt dann aber die anzahl der geschriebenen bytes nicht mehr. short hat ja nur 2 bytes oder ? (32 bit).
    mal gucken ob man put_num komplett ersetzen kann.

    Meep Meep



  • Wieso klappt das jetzt nicht, indem man operator<<, operator>> für die eigenen Klasse mit write und read überlädt? Das habe ich nicht verstanden. Oder soll das Ding für alle, auch nativen Typen funktionieren?



  • Irgendwie blick ich deinen Code nicht durch. Für was brauchst du denn f und fill in do_put() ? Du reservierst Speicher für binary_num_put . Wo ist der dazugehörige delete ? Und warum nimmst du keine Templates?



  • [Rewind] schrieb:

    Irgendwie blick ich deinen Code nicht durch. Für was brauchst du denn f und fill in do_put() ?

    Damit er die Methode überschreibt.

    Du reservierst Speicher für binary_num_put. Wo ist der dazugehörige delete ?

    Wird vom locale übernommen.

    Und warum nimmst du keine Templates?

    Wofür?


Log in to reply