inheriting ostream and streambuf problem with xspu

2019-04-08 04:25发布

问题:

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 streambufs, 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;
}