views:

616

answers:

3

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 STL 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 STL implementation. What is the recommended way to configure a stream to write its contents to a given buffer?

+1  A: 

As the link you posted says: "specific implementations may vary".

Can you not simply return the std::string object and then use std::string::c_str() or std::string::data() at the point the char buffer is required?

Alternatively use sprintf() from the C library, then the whole operation can be completed in the buffer passed. Since that way may result in potential buffer overrun, and you are using Visual C++, you might consider sprintf_s

Clifford
Unfortunately, I can not use `c_str()` as the buffer is owned by another type. I'm expected to fill that buffer with something so that the other type may operate upon it. `sprintf_s()` will work well and I shouldn't have a problem using that.
Steve Guidi
+3  A: 

Looks like a job for the (officially deprecated, but still standard) std::strstream. You could also look at the Boost.IOStreams library, array_sink, in particular.

Éric Malenfant
Isn't std::strstream simply a synonym of std::stringstream? Why would it be implemented any differently?
Clifford
No, it is more efficient (and tricky to use) as it allows the user to directly provide and manage the buffer. See option #4 in http://www.gotw.ca/publications/mill19.htm, for example.
Éric Malenfant
+3  A: 

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 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, 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;
}
Steve Guidi