Question

Background: I'm trying to optimize a logging system so that it uses memory-mapped files. I need to provide an std::ostream-like interface so that the logging system can write to that memory. I have identified std::strstream (which is deprecated though) and boost::iostreams::basic_array_sink could fit my needs.

Now I want to have the logging cyclic, meaning that when the output pointer is near the end of the memory block it should start over at the beginning again. My question is where would be the best point to start in order to implement this specific behaviour.

I'm rather overwhelmed by the std::iostreams class hierarchy and don't grasp all the internal workings as for now. I'm uncertain to whether i should/need to derive from ostream, streambuf, or both? Are these made for being derived from, anyway?

Or using boost:iostreams, would i need to have to write my own Sink?


EDIT: The following attempt compiles and produces the expected output:

class rollingstreambuf : public std::basic_streambuf<TCHAR>
{
public:
    typedef std::basic_streambuf<TCHAR> Base;

    rollingstreambuf(Base::char_type* baseptr, size_t size)
    {
        setp(baseptr, baseptr + size);
    }

protected:
    virtual int_type overflow (int_type c)
    {
        // reset position to start of buffer
        setp(pbase(), epptr());
        return putchar(c);
    }

    virtual std::streamsize xsputn (const char* s, std::streamsize n)
    {
        if (n >= epptr() - pptr())
            // reset position to start of buffer
            setp(pbase(), epptr());
        return Base::xsputn(s, n);
    }
};

char buffer[100];
rollingstreambuf buf(buffer, sizeof(buffer));
std::basic_ostream<TCHAR> out(&buf);

for (int i=0; i<10; i++)
{
    out << "Mumblemumble " << i << '\n';
}
out << std::ends;  //write terminating NULL char

Printing the buffer gives:

Mumblemumble 6
Mumblemumble 7
Mumblemumble 8
Mumblemumble 9

(which confirms the roll-over has taken place)

What it does is that it makes the streambuf use the provided buffer as a cyclic output buffer (put area), without ever advancing the buffer window in the output sequence (stream). (Using terminology from http://en.cppreference.com/w/cpp/io/basic_streambuf)

Now i feel very uncertain about the robustness and quality of this implementation. Please review and comment it.

Was it helpful?

Solution

This is a valid approach. overflow() should return:

traits::eof() or throws an exception if the function fails. Otherwise, returns some value other than traits::eof() to indicate success.

E.g.:

virtual int_type overflow (int_type c)
{
    // reset position to start of buffer
    setp(pbase(), epptr());
    return traits::not_eof(c);
}

xsputn() should probably write the beginning of the sequence to the end of the buffer, then rewind and write the remaining sequence to the front of the buffer. You could probably get away with the default implementation of xsputn() that calls sputc(c) for each character and then overflow() when the buffer is full.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top