Search code examples
c++visual-c++stream

Setting the internal buffer used by a standard stream (pubsetbuf)


I'm writing a subroutine that needs to write data to an existing buffer, and I would like to use the stringstream class to facilitate the formatting of the data.

Initially, I used the following code to copy the contents of the stream into the buffer, but would like to avoid this solution as it copies too much data.

#include <sstream>
#include <algorithm>

void FillBuffer(char* buffer, unsigned int size)
{
    std::stringstream message;
    message << "Hello" << std::endl;
    message << "World!" << std::endl;

    std::string messageText(message.str());
    std::copy(messageText.begin(), messageText.end(), buffer);
}

This is when I discovered the streambuf::pubsetbuf() method and simply rewrote the above code as follows.

#include <sstream>

void FillBuffer(char* buffer, unsigned int size)
{
    std::stringstream message;
    message.rdbuf()->pubsetbuf(buffer, size);

    message << "Hello" << std::endl;
    message << "World!" << std::endl;
}

Unfortunately, this does not work under the C++ standard library implementation that ships with Visual Studio 2008; buffer remains unchanged.

I looked at the implementation of pubsetbuf and it turns out that it literally "does nothing".

virtual _Myt *__CLR_OR_THIS_CALL setbuf(_Elem *, streamsize)
{   // offer buffer to external agent (do nothing)
    return (this);
}

This appears to be a limitation of the given C++ standard library implementation. What is the recommended way to configure a stream to write its contents to a given buffer?


Solution

  • After some more research on this problem, and scrutiny of my code, I came across a post suggesting the use of a hand-coded std::streambuf class. The idea behind this code is to create a streambuf that initializes its internals to refer to the given buffer. The code is as follows.

    #include <streambuf>
    
    template <typename char_type>
    struct ostreambuf : public std::basic_streambuf<char_type, std::char_traits<char_type> >
    {
        ostreambuf(char_type* buffer, std::streamsize bufferLength)
        {
            // set the "put" pointer the start of the buffer and record it's length.
            setp(buffer, buffer + bufferLength);
        }
    };
    

    Now if you look at my original code, you will notice that I didn't really need a stringstream to begin with. All I really needed was a way to write to an external buffer using the IOStream library and std::ostream is a much better type to address this problem. Incidentally, I suspect this is how the array_sink type from Boost.IOStreams is implemented.

    Here is the modified code that uses my ostreambuf type.

    #include <ostream>
    #include "ostreambuf.h"  // file including ostreambuf struct from above.
    
    void FillBuffer(char* buffer, unsigned int size)
    {
        ostreambuf<char> ostreamBuffer(buffer, size);
        std::ostream messageStream(&ostreamBuffer);
    
        messageStream << "Hello" << std::endl;
        messageStream << "World!" << std::endl;
    }
    

    For a more complex example see: