Search code examples
c++iostreamostreamstreambuf

Taking ownership of streambuf/stringbuf data


I'd like an interface for writing to an automatically resizing array. One way to do this is with a generic std::ostream *.

Then consider if ostringstream is the target:

void WritePNG(ostream *out, const uint8_t *pixels);

void *WritePNGToMemory(uint8_t *pixels)
{
  ostringstream out;
  WritePng(&out, pixels);

  uint8_t *copy = new uint8_t[out.tellp()];
  memcpy(copy, out.str().c_str(), out.tellp()];
  return copy;
}

But I want to avoid the memcpy(). Is there a way to take ownership of the array in the underlying stringbuf class and return that?

I get the feeling this can't be done using standard library, since the stream buffer might not even be a contiguous array.


Solution

  • IIRC the whole reason stringstream exists (vs strstream) was to sort out the fuzzy questions of memory ownership that would come up by giving direct buffer access. e.g. I think that change was to specifically prevent what you are asking to do.

    One way or another I think you'd have to do it yourself, by overriding the stream buffer. To answer a similar question I suggested something for input streams that wound up getting quite a few upvotes. But honestly I didn't know what I was talking about then, nor now when I suggest the following:

    Hacking up this link from the web for doing an "uppercasing stream buffer" to one that just echoes and gives you a reference to its buffer might give:

    #include <iostream>
    #include <streambuf>
    
    class outbuf : public std::streambuf {
        std::string data;
    
    protected:
        virtual int_type overflow (int_type c) {
            if (c != EOF)
                data.push_back(c);
            return c;
        }
    
    public:
        std::string& get_contents() { return data; }
    };
    
    int main() {
        outbuf ob;
        std::ostream out(&ob);
        out << "some stuff";
        std::string& data = ob.get_contents();
        std::cout << data;
        return 0;
    }
    

    I'm sure it's broken in all kinds of ways. But the uppercase-buffer-authors seemed to think that overriding the overflow() method alone would let them uppercase all output to the stream, so I guess one could argue that it's enough to see all output if writing to one's own buffer.

    But even so, going one character at a time seems suboptimal...and who knows what overhead you get from inheriting from streambuf in the first place. Consult your nearest C++ iostream expert for what the actual right way is. But hopefully it's proof that something of the sort is possible.