tags:

views:

18

answers:

1

Hi,

I need to get all the contents from a stream, without actually extracting them (just like stringstream::str()). I've tried basic_stringbuf::str(), but it behaves incorrectly when the stream is empty. To avoid that case, I had a go at basic_stringbuf::in_avail(), but that hasn't worked out very well either.

In the following test case, in_avail() doesn't return the number of available elements on the stream, and str() returns more elements than what is currently there:

#include <iostream>
#include <iterator>
#include <vector>
#include <sstream>

// extracts everything from the stream
std::vector<unsigned char> stream2vector(std::basic_istream<unsigned char>& stream)
{
    std::vector<unsigned char> retreivedData;
    std::istreambuf_iterator<unsigned char> it(stream);
    const std::istreambuf_iterator<unsigned char> endOfStream;
    retreivedData.insert(retreivedData.begin(), it, endOfStream);
    return retreivedData;
}

int main() {
    std::basic_stringbuf<unsigned char> buf;
    std::basic_iostream<unsigned char> stream(&buf);
    unsigned char array[5] = { 1, 2, 3, 4, 5 };
    stream.write(array, 5);


    std::cout << "rdbuf()->in_avail(): " << buf.in_avail() << "\n";
    std::vector<unsigned char> d1 = stream2vector(stream);
    std::cout << "d1.size(): " << d1.size() << "\n";

    std::cout << "\n";
    // d2 should be empty
    std::vector<unsigned char> d2 = stream2vector(stream);
    std::cout << "d2.size(): " << d2.size() << "\n";
    std::basic_string<unsigned char> s = buf.str();
    std::cout << "buf.str().size(): " << buf.str().size() << "\n";
}

Compiling on g++ 4.4, the output is:

rdbuf()->in_avail(): 1 // expected: 5
d1.size(): 5 // as expected

d2.size(): 0 // as expected
buf.str().size(): 5 // expected: 0

What am I doing wrong? What's the best way to do what I'm trying?

Thanks a lot.

A: 

in_avail is the number of characters ready to be read from the buffer, not the size of the buffer itself. It's really allowed to return any nonzero value here.

However, I can't answer what the best way is of what you're doing, because I don't know what you're doing. If you already have things as an unsigned char array, then you're going to want to do:

std::vector<unsigned char> data(array, array + sizeof(array)/sizeof(unsigned char));

If you're just trying to read a whole stream into a vector, then I would do exactly what you're doing; I'd just replace you're stream2vector function with this, equivalent, simpler one:

// extracts everything from the stream
std::vector<unsigned char> stream2vector(std::basic_istream<unsigned char>& stream)
{
    std::istreambuf_iterator<unsigned char> it(stream);
    const std::istreambuf_iterator<unsigned char> endOfStream;
    return std::vector<unsigned char>(it, endOfStream);
}

I'm not entirely sure why you're specializing every operation here for unsigned char -- I would just use the default char versions, because unsigned char is allowed to be the same size as a short, which is probably not what you want (but I am not aware of any implementation that does this).

Billy ONeal
Thanks for the comments. What I'm trying to do is read the whole contents of the stream without actually extracting them, which is what `stream2vector` currently does. That way, I can repeatedly call `stream2vector` on a given stream (as the example shows, this isn't possible with this iterator-based implementation).
Pedro d'Aquino
Pedro d'Aquino
@Pedro: Okay, then just save the get pointer position (with `tellg`) and then use `seekg` when you're done to reposition the get pointer.
Billy ONeal