33

I have some data in a buffer pointed to by a const char* pointer. The data is just an ASCII string. I know its size. I would like to be able to read it in the same way data is read from streams. I'm looking for a solution that would allow me to write code like this:

// for example, data points to a string "42 3.14 blah"
MemoryStreamWrapper in(data, data_size);
int x;
float y;
std::string w;
in >> x >> y >> w;

Important condition: the data must not be copied or altered in any way (otherwise I'd just use a string stream. To my best knowledge, it isn't possible to create a string stream from a const char pointer without copying the data.)

3

3 Answers 3

73

The way to do this is to create a suitable stream buffer. This can, e.g., be done like this:

#include <streambuf>
#include <istream>

struct membuf: std::streambuf {
    membuf(char const* base, size_t size) {
        char* p(const_cast<char*>(base));
        this->setg(p, p, p + size);
    }
};
struct imemstream: virtual membuf, std::istream {
    imemstream(char const* base, size_t size)
        : membuf(base, size)
        , std::istream(static_cast<std::streambuf*>(this)) {
    }
};

The only somewhat awkward thing is the const_cast<char*>() in the stream buffer: the stream buffer won't change the data but the interface still requires char* to be used, mainly to make it easier to change the buffer in "normal" stream buffers. With this, you can use imemstream as a normal input stream:

imemstream in(data, size);
in >> value;
29
  • 3
    @ildjarn: it has defined behavior on all platforms rather than different behavior on different platforms: std::stringbuf::setbuf() is implementation defined according to 27. 8.2.4 [stringbuf.virtuals] paragraph 15. Commented Oct 24, 2012 at 23:10
  • 10
    I am implementing something based on this, but I really need a working tellg() / tellp() for any istream / ostream instances that wrap the membuf. I've seen some indication that I need to implement seekoff and seekpos, but no solid working examples of that being done. Any insight? Commented Jun 30, 2015 at 20:08
  • 5
    @Andrew: the code doesn't allocate any memory, hence it doesn't deallocate any. The change is specifically wrong when data happens to point to a string literal. Commented Jan 30, 2017 at 3:50
  • 7
    @Andrew: whoever allocates data (if it is allocated) is responsible for deallocating it. Since the code snippet doesn't show where the data comes from it shall not assume and do something! The code is clearly incomplete as there is no declaration of data or size. For the question and the answer the source of these doesn't matter nor does it matter what happens to them after they have been used. Commented Jan 30, 2017 at 13:39
  • 7
    @Andrew: does it defeat the purpose? The short snippet showed all the relevant bits needed to implement a production-ready version of the class. Surely, the production-ready version of the code comes with documentation, test-cases, etc. I don't think an answer needs to cover everything it using. Commented Jan 30, 2017 at 13:59
4

As of C++23

Use std::ispanstream: It is a streambuf implementation provided by the standard library exactly for this purpose; using fixed buffers as iostreams. For input streams, these can be constant buffers.

2

The only way would be to subclass std::istream (which also requires subclassing std::streambuf) to create your own stream class that reads from constant memory.

It's not as easy as it sounds because the the C++ standard library stream classes are pretty messy and badly designed. I don't think it's worth it unless you need it to scale a lot.