klasse mit den istream/ostream operatoren kompatibel machen



  • otze schrieb:

    man könnte auch den string abfangen-.-

    Und eben gerade das geht nicht, da der ostream&operator<<(ostream&, const string&) eine globale Funktion in <string> ist die ostream::put() benutzt. Wenn du da deinen eigenen Stream schreibst weist du immer noch nicht wann das put von einem string kommt und wann nicht.

    Das einzige was mir hier einfällt wäre ein komplet neuer Stream der dann mit Templates die operator<<s von ostream nutzen könnte allerdings per Templatespezialisirung könntest du manche Typen anders implementiren.

    #include<iostream>
    #include<sstream>
    #include<string>
    using namespace std;
    
    class Stream{};
    
    template<class T>
    Stream&operator<<(Stream&,const T&);
    
    template<>
    Stream&operator<< <string>(Stream&out,const string&str)
    {
    	//...
    	return out;
    }
    
    template<class T>
    Stream&operator<<(Stream&out,const T&t)
    {
    	ostringstream old_out;
    	old_out<<t;
    	out<<old_out.str();
    	return out;
    }
    
    int main()
    {
    	Stream out;
    	out<<5;
    	out<<"Hallo";
    }
    

    Halte ich aber für keine gute Idee.



  • im gegenteil, ich finde diese idee sogar ziemlich gut. sowas ähnliches hatte ich im sinn, nur komplizierter.

    //edit kommando zurück. bei komplexen objekten die innerhalb des op<< nochn string weiterreichen, hat das system keine chance 😛

    mmm...kann man dem standardkommitee noch vorschläge unterbreiten? dann würde glatt vorschlagen, istream und ostream zu virtuellen basisKlassen zu machen 😉



  • würde glatt vorschlagen, istream und ostream zu virtuellen basisKlassen zu machen

    Wozu? Das Stream/Streambuffer-Duo-Konzept ist so ausgelegt, dass du neue Streambuffer ableitest und die mit neuen oder vorhandenen Streams verbindest. Es gibt aber keinen Grund dafür die I/O-Operatoren überschreiben zu wollen.

    das problem ist jetzt, dass die operatoren in istream/ostream nicht virtual sind,ich kann sie also nicht überschreiben

    Man überschreibt auch nicht die Operatoren sondern die virtuellen Funktionen des entsprechenden Streambuffers bzw. des entsprechenden Facets die indirekt von den Operatoren aufgerufen werden.

    Einen neuen streambuffer zu erstellen, stell ich mir auch nicht so toll vor,ganz einfach deshalb,weil ich damit keinerlei zugriff auf irgendwelche typinformationen hätte,und ich somit nicht reagieren könnte.

    Weißt du überhaupt was ein Streambuffer (z.B. std::basic_streambuf) ist? Und in welchem Zusammenhang ein solcher Streambuffer zu einem Stream steht?



  • du erzählst mir nichts neues. ich _habe_ mich im vorfeld damit beschäftigt.

    Weißt du überhaupt was ein Streambuffer (z.B. std::basic_streambuf) ist? Und in welchem Zusammenhang ein solcher Streambuffer zu einem Stream steht?

    ein streambuffer koordiniert die eingabe/ausgabefunktionen eines streams nach aussen hin. Dh wenn man zb in ein normales editfeld eine ausgabe machen wollte, so könnte man eifach einen neuen streambuffer erstellen, und hat somit eine einfache möglichkeit, die augabe ins fenster umzuleiten.
    Der streambuffer nimmt daten nur in charform an, und gibt sie auch nur in charform zurück.

    Man überschreibt auch nicht die Operatoren sondern die virtuellen Funktionen des entsprechenden Streambuffers bzw. des entsprechenden Facets die indirekt von den Operatoren aufgerufen werden.

    da in meinem fall die überladung des streambuffers nichts bringt,was absolut klar ist, bleiben nur die Facets, zu denen ich grad erst anständige infos gefunden hab.(die suche hat nur posts von dir ausgespuckt, zum glück war ein guter link dabei 😃 ).
    Mit Facettes scheint einiges zu gehen, doch obs genau das machen kann, was ich machen will, ist die Frage.

    An der sache mit string::operator<</>> kann ich so nichts ändern, ausser ich gebe meiner abgeleiteten Klasse einen op<<(string),und hoffe, dass der compiler die richtige version aussucht. wär zwar nicht besonders schön,aber es würde das oben besprochene problem lösen, und facets helfen mir dabei auch nicht.

    aber bei dem anderen problem können sie vielleicht was ändern:
    wenn man 2 ints hintereinander einliest, werden sie nicht in ihrer Bitform gespeichert,sondern in zeichenform ausgeschrieben. das es beim einlesen der beiden zahlen dann probleme geben sollte ist klar, denn wer weis schon bei dem string "123456" welche zahlen sich dahinter verstecken?(der stringstream weis es nicht,und fstream ebensowenig :D). können facets nun ein lesen und schreiben in Bitform ermöglichen? sodass ein int also genau 32Bit in der datei groß ist,und somit eindeutig auslesbar ist? Ich will nämlich nicht noch weiter mit read rumhantieren, das ist im code unlesbar!

    wenn nicht, dann fordere ich wirklich eine virtuelle baseclass von istream und ostream für den ottonormalprogrammierer 😉



  • ideallösung rein von der benutzung her wäre natürlich sowas:

    ofstream file("test.txt");
    
    Bitstream str(file.rdbuf());
    str<<100;
    str<<200;
    
    std::string s("Hallo");
    str<<s;
    
    //irgendwo anders dann
    ifstream file("test.txt");
    
    Bitstream str(file.rdbuf());
    int i1;
    int i2;
    std::string s;
    
    str>>i1>>i2>>s;
    

    wird wahrscheinlich aber nur ein Traum bleiben 😞



  • An der sache mit string::operator<</>> kann ich so nichts ändern

    Sicher kannst du. Die Operatoren nutzen das ctype-Facets des Streams. Ändere das Verhalten von isspace und schon ändert sich das Verhalten der Operatoren.

    So wie ich das sehe, willst du über die formatierte I/O die für das formatiere Lesen und Schreiben von Werten zuständig ist unformatierte Repräsentationen lesen und schreiben. Und das erscheint mir irgendwie der falche Ansatz.

    Wie auch immer. Grundsätzlich hast du erstmal einen *Zeichenstrom* (keinen Bytestrom) und zwei Möglichkeiten auf diesen einzuwirken.
    Die Funktionen für die formatierte I/O wandeln Werte in Zeichen bzw. Zeichen in Werte. Die Kodierung ist dabei relativ simple und nicht eindeutig.
    Du kännst die Formatierung ändern, in dem du die entsprechenden Facets änderst z.B. num_put, num_get usw.

    Alternativ hast du die Funktionen für die unformatierte I/O. Damit macht man in der Regel sowas wie du es hier willst: Z.B. nicht den Wert eines ints (z.B. 123) sondern seine platformabhängige Repräsentation (32-bit) in den Strom schreiben.

    Daraus ergeben sich imo drei Möglichkeiten
    a) Du verwendest die vorhandenen Funktionen für die unformatierte Ein-/Ausgabe (read, write...)
    b) Du schreibst dir deine eigenen Facets (und ein locale).
    c) Du definierst dir einen eigenen Character-Typ (und deine eigenen Char-Traits). In diesem Fall kannst du dann sogar die iostream-Template spezialisierien und damit auch die entsprechenden Operator-Funktionen.
    Ein Beispiel für die Verwendung einen eigenen Character-Typs findest du z.B. in
    Jack Reeves XDR-Stream (Die Artikel sind imo alle online erhältlich - siehe CUJ).

    PS: Was würden dir eigentlich virtuelle op>>/<<-Funktionen bringen? Keiner kann dir garantieren, dass die Implementation meines op<< und op>> meiner Klasse Foo intern auf die Standard-Ops aufsetzt. Was machst du dann?

    PPS: Hier der Link zum XDR-Stream-Einstiegsartikel: http://www.cuj.com/documents/s=7993/cujcexp1907reeves/



  • PS: Was würden dir eigentlich virtuelle op>>/<<-Funktionen bringen? Keiner kann dir garantieren, dass die Implementation meines op<< und op>> meiner Klasse Foo intern auf die Standard-Ops aufsetzt. Was machst du dann?

    heulen ;). nein natürlich nicht. dann muss ichs hinnehmen, was nicht geht, geht nicht. aber es gibt sachen, die ich garantieren kann:

    alle von den bitstreams nativ implementierte op<< sind mittels op>> umkehrbar.
    alle op<< die über die nativen op<< implementiert sind,sind umkehrbar.

    dass schreiben von einzelnen zeichen/zeichenketten ist nur umkehrbar,solange sichergestellt werden kann, dass genausoviele bytes wieder eingelesen werden. Ansonsten ist das weitere verhalten des streams undefiniert.

    damit könnte ich schon weitaus mehr garantieren, als es die stl streams können.

    a) Du verwendest die vorhandenen Funktionen für die unformatierte Ein-/Ausgabe (read, write...)

    die kenn ich schon...fehleranfällig,unhandlich, schwer lesbar-im endeffekt eigentlich unbrauchbar,wenn man wirklich eine rein unformatierte ein/ausgabe braucht.

    b) Du schreibst dir deine eigenen Facets (und ein locale).

    möglich, wenn auch etwas unhandlich 😃

    c) Du definierst dir einen eigenen Character-Typ (und deine eigenen Char-Traits). In diesem Fall kannst du dann sogar die iostream-Template spezialisierien und damit auch die entsprechenden Operator-Funktionen.
    Ein Beispiel für die Verwendung einen eigenen Character-Typs findest du z.B. in
    Jack Reeves XDR-Stream (Die Artikel sind imo alle online erhältlich - siehe CUJ).

    auch wieder möglich, doch wie läuft die zusammenarbeit zwischen 2 streams mit unterschiedlichen traits?



  • nachtrag:

    im von dir genannten artikel wird auch nur von basic_ios<> abgeleitet.
    das ist nicht ganz das,was ich suche, aber er hat am rande was ganz interesaantes erwähnt,was ich ganz vergessen hatte: template spezialisation, basic_istream ist doch ein template 😮. und du schreibst das auch noch, und ich überlese das dann auch...manchmal muss man doch bei mir den Holzhammer rausholen,der zaunpfahl reicht nichmehr 😃

    das problem ist nur:
    sieht jeder op<<//op>> so aus?

    template<class char_type,class traits>
    basic_istream<char_type,traits> operator<<(basic_istream<char_type,traits>&,foo&){
    //...
    }
    


  • Soweit ich weiß sind (können) die Operatoren für int, char und char* als Memberfunktionen implementiert.



  • Ben04 schrieb:

    Soweit ich weiß sind (können) die Operatoren für int, char und char* als Memberfunktionen implementiert.

    memberfunktionen von was? und jetzt nicht etwas, was ich nicht schon lange weis 🙂

    falls du meinstest, was ich denke, das du meinen wolltest,dann liegst du falsch, da ich die operatoren von externen kalssen wie string meinte.



  • otze schrieb:

    Ben04 schrieb:

    Soweit ich weiß sind (können) die Operatoren für int, char und char* als Memberfunktionen implementiert.

    memberfunktionen von was?

    Na 3 Mal darfst du ratten. Wenn der A operator<<(B,C) keine globale Funktion ist dann kann es nur A B::operator<<(C) sein.

    otze schrieb:

    und jetzt nicht etwas, was ich nicht schon lange weis 🙂

    otze schrieb:

    manchmal muss man doch bei mir den Holzhammer rausholen,der zaunpfahl reicht nichmehr 😃

    Bei solchen Methoden vergisst man schon mal gern was 😃 .

    otze schrieb:

    falls du meinstest, was ich denke, das du meinen wolltest,dann liegst du falsch, da ich die operatoren von externen kalssen wie string meinte.

    "jeder" heist für mich alle und bei "alle" sind für mich int, char und char* einbegriffen.


Anmelden zum Antworten