A
ok, also nachdem ich jetzt
http://www.angelikalanger.com/IOStreams/Excerpt/excerpt.htm
mal gründlicher gelesen hab (hatte es vorher nur überflogen) muss ich sagen: das war goldrichtig! (danke!)
Also um die Frage jetzt auch für diejenigen zu beantworten, die durch google & co hier landen:
puffer für istreams müssen überschreiben:
streamsize xsgetn(char* s, streamsize n)
holt die nächsten [i]n[/i] Zeichen aus dem Puffer, schreibt sie in das Array
[i]s[/i] und gibt die anzahl der zeichen aus, die geholt werden konnten (können
ja zB beim end-of-file weniger sein, als angefordert wurden)
wenn währenddessen ein unterlauf passiert (alle zeichen aus dem puffer gelesen
wurden), muss zB mit underflow() nachgeladen werden.
int_type underflow()
wird aufgerufen, wenn man versucht, aus einem leeren puffer zu lesen - diese
methode soll den internen puffer wieder auffüllen mit den nächsten daten aus
dem stream.
rückgabewert ist das erste zeichen, das nachgeladen wurde
int_type pbackfail(int_type c)
soll dafür sorgen, dass daten in den stream zurückgeschrieben werden können
(indem zB der puffer vergrößert wird mit leerem platz vorne) und das zeichen
[i]c[/i] hineingeschrieben wird.
die methode wird aber nur aufgerufen, wenn man versucht, etwas VOR dem ersten
zeichen des puffers zurückzuschreiben, was man laut standard auch als fehler
definieren darf und einfach die methode der basisklasse übernehmen kann, die
dann einen fehler meldet. (dann braucht man die methode also nicht zu
überschreiben)
und natürlich konstruktoren/destruktoren, die von den konstruktoren des
abgeleiteten istreams aufgerufen werden - diese müssen auch den speicher für
den puffer allozieren / freigeben
puffer für ostreams müssen überschreiben:
streamsize xsputn (const char* s, streamsize n)
schreibt [i]n[/i] zeichen aus dem array [i]s[/i] in den Puffer und gibt zurück,
wie viele zeichen geschrieben werden konnten. wenn der puffer zwischendurch
überläuft, soll er geflusht werden und weiter befüllt werden (der rückgabewert
soll also nicht anzeigen, wie viel geschrieben werden konnte, bis der puffer
voll war, sondern wenn der stream keine weiteren inputs angenommen hat - zB
wenn die festplatte voll ist)
zum flushen kann man overflow (s.u.) benutzen
int_type overflow(int_type c)
wird aufgerufen, wenn versucht wird, in einen vollen puffer zu schreiben. es
soll dann neuer platz im puffer beschafft werden (zB indem er bei einem
filestream in die datei geschrieben wird oder indem er bei einem stringstream
vergrößert wird) und das zeichen [i]c[/i] soll in den neu geschaffenen platz
geschrieben werden.
und natürlich konstruktoren/destruktoren, die von den konstruktoren des
abgeleiteten ostreams aufgerufen werden - diese müssen auch den speicher für
den puffer allozieren / freigeben
wobei ich mal sagen muss, dass ich die klassenhierarchie ziemlichen quatsch finde - wieso definiert streambuf methoden für input UND output und steuert dann über methoden-parameter, welche zeiger jetzt benutzt werden sollen?
imho sollten besser zwei klassen davon abgeleitet werden - eine für input-streambufs und eine für output-streambufs...
denn rein konzeptionell ist das doch ne verkehrte welt - die basisklasse hat sachen, die in den abgeleiteten klassen nicht verwendet werden. normal sollte es doch umgekehrt sein - dass die abgeleitete klasse neue dinge hinzufügt...
genauso finde ich es sinnfrei, dass die klasse grundsätzlich davon ausgeht, dass man sachen in den stream zurückschreiben kann - und wenn ein konkreter stream das nicht kann muss er dann halt fehler melden... warum wird nicht einfach ne klasse rewindable_streambuf und peekable_streambuf abgeleitet, aus denen man zusammen ein seekable_streambuf ableiten könnte...
es scheint einfach so, als wäre diese klasse gezielt auf stringbuffer hin entwickelt worden, anstatt für abstrakte streams (schon filestreams dürfen dem standard zufolge ja das zurückschreiben verweigern...)
ausserdem geht zumindest die implementierung in der GNU Compiler Collection davon aus, dass der Puffer mittels eines char-arrays realisiert wird - so hat die klasse schon einige charT pointer, die von den NICHT-VIRTUELLEN (also den nicht überschreibbaren!) methoden benutzt werden... man kann die puffer also wohl gar nicht anders definieren als mit arrays - was ich zB bei sockets mist finde, weil die schon eigene puffer (im kernel oder in der hardware - bin ich nicht sicher) haben, auf die man hier zurückgreifen könnte, indem man die methoden einfach auf die funktionen für sockets weiterleitet - aber NEEEEEEIN....
wenn die klasse schon so fest davon ausgeht, dass charT* benutzt wird - WARUM alloziert der (protected) konstruktor dann nicht gleich den RAM dafür? ok, vielleicht weil man getrennte oder gemeinsame puffer für input und output benutzen könnte... aber da wären wir wieder bei der dämlichen klassenhierarchie - man sollte ne basisklasse haben, die davon ausgeht, dass man in den stream weder was reinschreiben noch rauslesen kann - davon abgeleitet: puffer für istreams (aus denen man zusätzlich lesen kann), ostreams (in die man zusätzlich schreiben kann) und synchrone streams (die einen gemeinsamen speicher für input und output haben) - dann könnte man nen puffer für asynchrone streams ableiten von den klassen für istreams und ostreams, die dadurch automatisch getrennte puffer hätte...
nicht zu vergessen, dass die puffer jeweils nur 3 pointer haben - einen für den anfang des puffers, einen für das nächste zu lesende character und einen für... ja wofür denn?
entweder der markiert bis wohin der puffer gefüllt ist (dann kann man beim nächsten underflow nicht mehr feststellen, wie viel platz man wieder befüllen kann) oder er markiert bis wohin der puffer alloziert ist (dann kommt der nicht damit klar, wenn weniger bytes aus dem stream kommen, als angefordert)
das ist wieder ein problem, das bei so ziemlich jedem stream von bedeutung ist, ausser bei stringstreams... wieder drängt sich mir also der verdacht auf, dass diese klasse hauptsächlich auf die stringstreams hin entwickelt wurde...