ostream erweitern
-
Ich brauche eine Klasse die von der Eingabe her wie ein Stream funktioniert, allerdings mit einem kleinen Unterschied. Ich möchte, dass vor jeder Zeile noch automatisch etwas ausgegeben wird wie z.B. das Datum oder ein Kommentar.
Das sollte dann so aussehen:
Eine Befehlssequenz wie die folgendeKlasse k(std::cout); // den Zielstream übergeben k << "Hallo";
sollte folgende Ausgabe auf stdout erzeugen:
01.08.2003 Test HalloIch habe überlegt wie ich das realisieren könnte bin aber auf keinen befriedigenden Ansatz gekommen.
Als eine Variante hatte ich, den Stream als Member quasi zu kapseln. Doch da müsste ich wohl den Op<< für alle möglichen Datentypen überladen.
Dann dachte ich mir, man könnte von ostream ableiten. Da würde ich dann den automatischen Kommentar vor den Stream schreiben, diesen Speichern bis flush oder endl kommt und dann komplett ausgeben um dann für die nächste Eingabe wieder genau einen Kommentar davor zu schreiben. Nur weiss ich nicht wie ich das im Detail machen kann und ob es überhaupt geht.
Hat irgendjemand eine Idee?
-
Ich habe überlegt wie ich das realisieren könnte bin aber auf keinen befriedigenden Ansatz gekommen
Na dann nenne ich dir einen: Implementiere einen eigenen Streambuffer.
Als eine Variante hatte ich, den Stream als Member quasi zu kapseln. Doch da müsste ich wohl den Op<< für alle möglichen Datentypen überladen.
Keine gute Idee.
Dann dachte ich mir, man könnte von ostream ableiten.
Keine gute Idee.
Hat irgendjemand eine Idee?
Frei nach Dietmar Kühl:
#include <iostream> #include <string> template <class PrefixOp> struct prefixbuf : std::streambuf { prefixbuf(PrefixOp op, std::streambuf* sb) : prefixOp_(op) , sbuf_(sb) , flag_(true) {} int_type overflow(int_type c) { if (!traits_type::eq_int_type(c, traits_type::eof())) { if (flag_) { std::string temp(prefixOp_()); sbuf_->sputn(temp.c_str(), temp.size()); } sbuf_->sputc(c); flag_ = traits_type::eq_int_type(c,traits_type::to_int_type('\n')); } return traits_type::not_eof(c); } int sync() { return sbuf_->pubsync(); } private: PrefixOp prefixOp_; std::streambuf* sbuf_; bool flag_; }; struct Time { std::string operator()() const { return "hier muss deine Datumsberechnung hin "; } }; int main() { std::cout.rdbuf(new prefixbuf<Time>(Time(), std::cout.rdbuf())); std::cout << "Hallo" << std::endl; std::cout << "Fred" << std::endl; }
Zum besseren Verständnis empfehle ich die Lektüre von:
http://www.langer.camelot.de/IOStreams/Excerpt/excerpt.htm
-
Danke für die Antwort.
Ich versuch mich jetzt hier durchzufitzen.
Könntest du evtl. in ner freien Minute noch den ein oder anderen Kommentar nachtragen? Das wäre sehr hilfreich.
-
Könntest du evtl. in ner freien Minute noch den ein oder anderen Kommentar nachtragen? Das wäre sehr hilfreich.
Was ist *nach* der Lektüre des geposteten Buch-Auszugs noch unklar?
Vorher macht das kommentieren keinen Sinn, da dort sonst deutlich mehr Kommentar als Code steht. Außerdem kann ich das mit Sicherheit nicht so schön erklären wie Kreft und Langer.
-
Hast recht, der Post war ja *vor* der Buchlektüre gewesen und aufgrund einiger Verwirrung, erzeugt durch deine *sprechenden* Namen, entstanden. Das hat sich somit erledigt.
Wieso nimmst du eigentlich not_eof statt eof?
return traits_type::not_eof(c);
Leider schreibt er jetzt allerdings nach jedem Zeilenumbruch den Zusatz vorn ran und mir ist bis jetzt auch noch keine bessere Lösung eingefallen für:
flag_ = traits_type::eq_int_type(c,traits_type::to_int_type('\n'));
Aber das ist auch nicht so wild.
Vielen Dank nochmal!
-
Ich hab noch eine Frage.
Was müsste ich tun, wenn ich den kompletten Stream (also nicht nur meinen Zusatz sondern auch alles andere was über << reingekommen ist) nur unter bestimmten Bedingungen ausgeben will?
Muss ich dafür die Funktion xsputn überschrieben oder gibt es einen anderen Weg?
-
@Drakos: Wie wärs mit std::endl vermeiden und stattdessen wenn ausgegeben werden soll std::flush übergeben? <flasch> (Geht aber nicht mit der gezeigten Implementierung) </flasch>
<flasch> @Hume: Der streambuf hat - soweit ich ihn verstanden habe - nicht die gewünschte Semantik. Obwohl er vermutlich ausreicht, kann man leicht falsches Verhalten erzwingen: cout << "bla" << flush << "bul" << endl; sollte damit [timestamp]bla[timestamp]bul\n ausgeben. Ist das so gewollt? </flasch>
EDIT: ich scheine ihn falsch gelesen zu haben... sorry für die beschuldigung.
-
Mr. N schrieb:
@Drakos: Wie wärs mit std::endl vermeiden und stattdessen wenn ausgegeben werden soll std::flush übergeben? <flasch> (Geht aber nicht mit der gezeigten Implementierung) </flasch>
Das würde mir nicht weiterhelfen, da die Ausgabe des Streams global geregelt werden soll, ohne die Ausgaben jedes Mal zu ändern. Kurz gesagt geht es hier darum, verschiedene Logging-Streams an- und auszuschalten.
-
Wenn du ausschalten willst, kannst du doch auch einen Null-Streambuf schreiben, der seine Daten ins Nirvana lässt. Solltest du die Daten aber doch wollen, gibts ja noch den Umweg über stringstreams...