Didar_Uranov
Didar_Uranov

Reputation: 1240

Visual Studio std::stringstream pubsetbuf does not work

pubsetbuf member of std::stringbuf is not working at all in Visual Studio 2010!

The code:

char *FileData = ... ;
unsigned long long FileDataLen = ... ;
std::stringstream *SS = new std::stringstream(std::stringstream::in | std::stringstream::out);
SS->rdbuf()->pubsetbuf( FileData, (std::streamsize)FileDataLen );

pubsetbuf does nothing in Visual Studio!!!

Workaround #1:

std::stringstream *SS = new std::stringstream( std::string(FileData, (size_type)FileDataLen ) ),std::stringstream::in | std::stringstream::out);

Workaround #2:

SS->rdbuf()->sputn(FileData, (streamsize)FileDataLen);

But both of these workarounds produce unnecessary memory copying. I definitely need a working pubsetbuf member of std::stringbuf.

Upvotes: 3

Views: 2329

Answers (4)

tim
tim

Reputation: 492

I recently encountered the same issue of setbuf not being implemented in Visual Studio 2017.

After some searching on Stack Overflow, I found a solution for an output stream that doesn't use copying of the buffer which I modified for an input stream. Here they are for reference.

Output Stream

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

#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);
    }
};

Input Stream

Source: Internal buffer used by standard input stream (pubsetbuf)

#include <streambuf>

template <typename char_type>
struct istreambuf : public basic_streambuf<char_type, char_traits<char_type>>
{
    istreambuf(char_type* buffer, streamsize buffer_length)
    {
        // Set the "get" pointer to the start of the buffer, the next item, and record its length.
        this->setg(buffer, buffer, buffer + buffer_length);
    }
};

int main()
{
    ifstream infile(FILENAME, ifstream::binary);

    // Read entire file into buffer.
    infile.seekg(0, ios::end);
    streampos length = infile.tellg();
    infile.seekg(0, ios::beg);
    vector<char> buffer(length);
    //char* buffer = new char[length];
    infile.read(&buffer[0], length);
    infile.close();

    // Create buffer and point local_stream to it.
    istreambuf<char> istream_buffer(&buffer[0], length);
    istream local_stream(&istream_buffer);

    string str1;

    while (local_stream >> str1)
    {
        . . .
    }
}

Upvotes: 0

Eduardo Lemus
Eduardo Lemus

Reputation: 21

I see the same thing. I'm working on scenarios that definitely can't afford making unnecessary data copies. This seems to be somehow intentional as per comments in basic_streambuf class:

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

Upvotes: 0

Sheer Ice
Sheer Ice

Reputation: 31

basic_ios.clear() If you need to change rdbuf, call this first or it'll fail to work.

std::ifstream file("file1.txt"); // file1.txt contains "Welcome!"
std::stringstream ss;
ss << file.rdbuf();

std::cout << ss.str() << std::endl;

Outputs "Welcome!".
Let's try again with a new file.

// Empty it
file.close();
ss.str("");

// New file
file.open("file2.txt"); // file2.txt contains "Goodbye!"
ss << file.rdbuf();

std::cout << ss.str() << std::endl;

Outputs nothing.

ss.clear();
ss << file.rdbuf();
std::cout << ss.str() << std::endl;
file.close();

Outputs "Goodbye!"

Upvotes: 3

Cubbi
Cubbi

Reputation: 47448

putsetbuf only makes sense for fstream (technically, for std::basic_filebuf), where the buffer and the stream are two different things.

For stringstream (technically, std::basic_stringbuf) they are one and the same std::string.

If you need a stream that works on a string that's external to it, consider std::strstream: or boost::iostreams::array_sink

Upvotes: 4

Related Questions