inheriting ostream and streambuf problem with xsputn and overflow
Asked Answered
R

1

7

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.

Rachal answered 12/4, 2011 at 21:52 Comment(1)
Part of the problem with this design is the assumption that you can identify individual values being streamed. When you have std::complex<T>::operator<<(ostream& os), it must call os << real << imag. Now what is the value? Logically the complex number is a single value, but <iostream> treats it as 2.Mcnary
C
20

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;
}
Champion answered 13/4, 2011 at 9:24 Comment(4)
That is basically what I am doing now, what I want to know is if there is anyway to tell how many times overflow is going to be called, or when the last char is coming past. For Example if I do mystream << 1.3f; I will get the characters '1' then '.' then '3' but inside the streabuf I have no way of knowing how many characters to expect. And after I get the 3 I want to act on the buffer (call out to another object letting it know it is ready)Rachal
There's no way the code can know in advance what the client code is going to do, or how many times it is going to be called. What you can do is set unitbuf on the ostream: this will cause sync to be called at the end of every operator<< (in the destructor of the sentry object). You then act on the buffer in sync.Champion
aha! that is EXACTLY what i needed! Marking this as the answer, cant up vote it yet though, I will come back and do so once I get enough rep. Thank you so much.Rachal
I think the sync function is problematic. It should also reset "pptr()". Take libstdc++ as an example github.com/gcc-mirror/gcc/blob/…. It should call overflow instead of write.Gulledge

© 2022 - 2024 — McMap. All rights reserved.