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

3

27

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?

Brittnee answered 29/9, 2009 at 18:30 Comment(4)
@Steve I am using VS2008 also, but it is implemented!Ney
@Arak -- My implementation of pubsetbuf() forwards to the private setbuf() operation, implemented as above. Can you confirm that your implementation is different?Brittnee
This still holds true in VS2010Wiebmer
Still true in VS2017Snowblink
B
20

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:

Brittnee answered 30/9, 2009 at 0:20 Comment(1)
Does this code not have an issue with null terminating the buffer ? Considering that this is a hello world example I guess the buffer should be null terminated.Arabesque
L
5

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.

Lucho answered 29/9, 2009 at 19:15 Comment(3)
Isn't std::strstream simply a synonym of std::stringstream? Why would it be implemented any differently?Goings
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 gotw.ca/publications/mill19.htm, for example.Elissa
@Clifford: kera.name/articles/2008/09/…Heaviside
G
0

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

Goings answered 29/9, 2009 at 18:41 Comment(1)
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.Brittnee

© 2022 - 2024 — McMap. All rights reserved.