tags:

views:

813

answers:

4

I have a memory block (opaque), that I want to store in a Blob in mySQL through their C++ adapter. The adapter expects a istream:

virtual void setBlob(unsigned int parameterIndex, std::istream * blob) = 0;

So my question is: how can I create a std::istream from this memory block (typed as char*). It's not a string as it is not null-terminated (but I know its length of course).

I could not find a way to do it without copying my memory block for example in a std::string. I think this is a bit wasteful. Something like this doesn't work:

    std::streambuf istringbuf(blockPtr, blockLength);
    std::istringstream tmp_blob(&istringbuf);

because std::streambuf doesnt have such a constructor. I saw the following suggestion.

    std:: istringstream       tmp_blob;
    tmp_blob.rdbuf()->pubsetbuf(blockPtr, blockLength);

Is that the correct way?

+3  A: 

Look at std::istrstream it has a constructor

 istrstream( char* pch, int nLength );
Mark
Yeah, but Josuttis says "The char* stream classes are retained only for backward compatibility. Their interface is error prone and they are rarely used correctly." This is why I was a bit reluctant to use them. And "retained only for backward compatibility" seems to imply there is a better way using "better" classes.
Jean-Denis Muys
The issue with strstream is that it is more complex to manage the memory of the char* buffer so in general prefer stringstream as it does the memory management for you. However in this case you are already managing the memory of the char* so the normal benefit is in this case a cost.In fact in this case strstream does exactly what you want ewith minimal overhead in code or speed.This is similar to the discussion of ostrsteream by Herb Sutter http://www.gotw.ca/publications/mill19.htm
Mark
I read that article, it's directly relevant. thanks. I guess my question is answered, though no comment on the "tmp_blob.rdbuf()->pubsetbuf(blockPtr, blockLength);" approach.
Jean-Denis Muys
+3  A: 

Boost.IOStreams has a stream that works like a stringstream, but wraps a native array, so you avoid having to copy the data.

std::stringstream always creates its own internal buffer

jalf
A: 

Untested but perhaps worth a test...

std::stringstream ss;
ss.write( blockPtr, blockLength );
ss.seekg(0);

Then call that setBlob function with ss. Your still have that internal buffer in std::stringstream as jalf already mentioned though.

Peter Jansson
+5  A: 

It's actually pretty trivial to write a one-shot std::streambuf that uses the buffer in place as the default behaviour of all the virtual functions of std::streambuf does 'the right thing'. You can just setg the read area in construction and underflow and uflow can safely be left to return traits_type::eof() as the end of the initial get area is the end of the stream.

e.g.:

#include <streambuf>
#include <iostream>
#include <istream>
#include <ostream>

struct OneShotReadBuf : public std::streambuf
{
    OneShotReadBuf(char* s, std::size_t n)
    {
        setg(s, s, s + n);
    }
};

char hw[] = "Hello, World!\n";

int main()
{
    // In this case disregard the null terminator
    OneShotReadBuf osrb(hw, sizeof hw - 1);
    std::istream istr(&osrb);

    istr >> std::cout.rdbuf();
}
Charles Bailey
Haha, your solution didn't come up in search results when I posted my question. We've got pretty much the same thing ;) http://stackoverflow.com/questions/2079912/simpler-way-to-create-a-c-memorystream-from-char-sizet-without-copying-th
Marcin Seredynski