问题
I have been doing research on creating my own ostream and along with that a streambuf to handle the buffer for my ostream. I actually have most of it working, I can insert (<<) into my stream and get strings no problem. I do this by implimenting the virtual function xsputn. However if I input (<<) a float or an int to the stream instead of a string xsputn never gets called.
I have walked through the code and I see that the stream is calling do_put, then f_put which eventually tries to put the float 1 character at a time into the buffer. I can get it to call my implementation of the virtual function overflow(int c) if I leave my buffer with no space and thereby get the data for the float and the int.
Now here is the problem, I need to know when the float is done being put into the buffer. Or to put it another way, I need to know when this is the last time overflow will be called for a particular value being streamed in. The reason xsputn works for me is because I get the whole value up front and its length. So i can copy it into the buffer then call out to the function waiting for the buffer to be full.
I am admittedly abusing the ostream design in that I need to cache the output then send it all at once for each inputted value (<<).
Anyways to be clear I will restate what I am shooting for in another way. There is a very good chance I am just going about it the wrong way.
I want to use an inherited ostream and streambuf so I can input values into it and allow it to handle my type conversion for me, then I want to ferry that information off to another object that I am passing a handle down to the streambuf to (for?). That object has expensive i/o so I dont want to send the data 1 char at a time.
Sorry in advance if this is unclear. And thank you for your time.
回答1:
It's not too clear what you're doing, although it sounds roughly
right. Just to be sure: all your ostream
does is provide
convenience constructors to create and install your streambuf
,
a destructor, and possibly an implementation of rdbuf
to
handle buffers of the right type. Supposing that's true:
defining xsputn
in your streambuf
is purely an optimization.
The key function you have to define is overflow
. The simplest
implementation of overflow
just takes a single character, and
outputs it to the sink. Everything beyond that is optimization:
you can, for example, set up a buffer using setp
; if you do
this, then overflow
will only be called when the buffer is
full, or a flush was requested. In this case, you'll have to
output buffer as well (use pbase
and pptr
to get the
addresses). (The streambuf
base class initializes the
pointers to create a 0 length buffer, so overflow
will be
called for every character.) Other functions which you might
want to override in (very) specific cases:
imbue
: If you need the locale for some reason. (Remember that
the current character encoding is part of the locale.)
setbuf
: To allow client code to specify a buffer. (IMHO, it's
usually not worth the bother, but you may have special
requirements.)
seekoff
: Support for seeking. I've never used this in any of
my streambuf
s, so I can't give any information beyond what
you could read in the standard.
sync
: Called on flush, should output any characters in the
buffer to the sink. If you never call setp
(so there's no
buffer), you're always in sync, and this can be a no-op.
overflow
or uflow
can call this one, or both can call some
separate function. (About the only difference between sync
and uflow
is that uflow
will only be called if there is
a buffer, and it will never be called if the buffer is empty.
sync
will be called if the client code flushes the stream.)
When writing my own streams, unless performance dictates
otherwise, I'll keep it simple, and only override overflow
.
If performance dictates a buffer, I'll usually put the code to
flush the buffer into a separate write(address, length)
function, and implement overflow
and sync
along the lines
of:
int MyStreambuf::overflow( int ch )
{
if ( pbase() == NULL ) {
// save one char for next overflow:
setp( buffer, buffer + bufferSize - 1 );
if ( ch != EOF ) {
ch = sputc( ch );
} else {
ch = 0;
}
} else {
char* end = pptr();
if ( ch != EOF ) {
*end ++ = ch;
}
if ( write( pbase(), end - pbase() ) == failed ) {
ch = EOF;
} else if ( ch == EOF ) {
ch = 0;
}
setp( buffer, buffer + bufferSize - 1 );
}
return ch;
}
int sync()
{
return (pptr() == pbase()
|| write( pbase(), pptr() - pbase() ) != failed)
? 0
: -1;
}
Generally, I'll not bother with xsputn
, but if your client
code is outputting a lot of long strings, it could be useful.
Something like this should do the trick:
streamsize xsputn(char const* p, streamsize n)
{
streamsize results = 0;
if ( pptr() == pbase()
|| write( pbase(), pptr() - pbase() ) != failed ) {
if ( write(p, n) != failed ) {
results = n;
}
}
setp( buffer, buffer + bufferSize - 1 );
return results;
}
来源:https://stackoverflow.com/questions/5642063/inheriting-ostream-and-streambuf-problem-with-xsputn-and-overflow