2
votes

In Qt library there QByteArray and QDataStream classes, which allow me to read and write variables to memmory buffer with very easy to use syntax:

QByteArray data = getData();
QDataStream stream( data );
double d = 0;
int i = 0;
stream >> d >> i;

How to implement simmilar behavior with only stl stream classes? For example I have a const char* data and size of it, so I want to construct std::istream and read varibles from that data:

const char* data = getData();
size_t size = getSize();

membuffer buf( data, data + size );
std::istream str( buf );
double d = 0;
str >> d;

Note that data should not be copied!

1
std::xstream classes have the functionality of QTextStream, not QDataStream. There's nothing in C++ standard library that resembles QDataStream. Generally speaking, std::istream will be useless for your purpose. Reimplementing QDataStream is not hard at all.Kuba hasn't forgotten Monica
Once the invalid assumptions are corrected, this is possibly a duplicate of this question.Kuba hasn't forgotten Monica
@KubaOber: given that the request was to do something like that using standard streams, question you pointed to (and even more so the answer) are clearly not the answer...Dietmar Kühl
@DietmarKühl Standard streams are a drop-in replacement for most of QTextStream. There's no point to them if all you want is binary serialization, and QDataStream does binary serialization. The question is based on an invalid assumption. Just because QDataStream and std::istream contain "stream" in their name, doesn't make one the replacement of the other.Kuba hasn't forgotten Monica

1 Answers

2
votes

Assuming you have a fixed sized data buffer and its size it is trivial to implement by creating a suitable stream buffer:

struct membuf: std::streambuf {
     membuf(char* begin, char* end) {
         this->setg(begin, begin, end);
     }
};

This simple stream buffer just sets up the stream buffers "get-area" to be the range [begin, end) (begin is used twice as it is possible to set up a "put-back area" which in this case is empty). When there are no more characters to read the stream will try to call underflow() whose default implementation indicates failure. If you want to read more characters you'd overwrite underflow() to provide more characters in a newly set up buffer.

With that you can create a stream using this memory backed stream buffer:

membuf       sbuf(begin, end);
std::istream in(&sbuf);
double d = 0;
if (in >> d) { // always check conversions from string to a value...
    // ...
}

For a bit of extra convenience, the creation of the stream buffer and the stream can also be bundled into a class. There is a small trick in that the stream buffer should be created reasonably earlier but it is doable:

class imemstream: private virtual membuf, public std::istream {
public:
    imemstream(char* begin, char* end)
        : membuf(begin, end)
        , std::ios(static_cast<std::streambuf*>(this))
        , std::istream(static_cast<std::streambuf*>(this)) {
    }
};

Just a warning, though: creating a stream is more expensive than copying quite a bit of data. That is, if you want to use that stream in a loop you probably want to provide functionality to reset the buffer (probably combined with clearing state flags).