Custom std::streambuf
C++ has stream classes that are useful for input and output operations on files, io devices and strings. For example, we can use std::ostream
to output to std::stringstream
. However, there’s no built-in way to pre-allocate output buffer of std::stringstream
which means there might be multiple allocations call as we output more bytes to the stream. Luckily, stream exposes the buffer that we can inherit and override some of the methods.
Doing a full and proper implementation of custom std::streambuf
is out of scope for this short article. I am just going to show a simple custom std::streambuf
that pre-allocates a fixed capacity buffer used for stream out. We will use std::string
to manage this custom string buffer and manipulate std::streambuf
internal put pointers (so that tellp()
will also work). The following protected members of std::streambuf can be used for manipulating put pointers (see https://www.cplusplus.com/reference/streambuf/streambuf/ for more complete description):
pbase()
- pointer to the beginning of output sequencepptr()
- pointer to current position of output sequenceepptr()
- pointer to end of output sequencepbump()
- increase put pointersetp()
- set output sequence pointers
Here’s the code:
class FixedStreamBuf : public std::streambuf
{
std::string& buffer;
public:
FixedStreamBuf(std::string& buffer, size_t bufferMaxSize)
: buffer(buffer)
{
buffer.resize(bufferMaxSize);
setp(buffer.data(), buffer.data() + buffer.size());
}
~FixedStreamBuf()
{
const size_t writtenSize = pptr() - pbase();
buffer.resize(writtenSize);
}
protected:
std::streamsize xsputn(const char* s, std::streamsize n) override
{
std::memcpy(pptr(), s, n);
pbump(n);
return n;
};
int_type overflow(int_type ch) override
{
// Fixed stream buffer doesn't handle overflow because we don't want to
throw std::runtime_error("FixedStreamBuf overflow!");
}
pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override
{
// Only output stream is supported
if (which != std::ios_base::out)
throw std::runtime_error("Not supported");
if (dir == std::ios_base::cur)
pbump(off);
else if (dir == std::ios_base::end)
setp(pbase(), epptr() + off, epptr());
else if (dir == std::ios_base::beg)
setp(pbase(), pbase() + off, epptr());
return pptr() - pbase();
}
};
Simple example on how to use this:
std::string streamData;
// write to streamData via std::ostream
{
FixedStreamBuf streamBuf(streamData, 1024);
std::ostream ostr(&streamBuf);
ostr << "Hello World!";
}
// do something with streamData
doSomething(streamData);
References
- std::streambuf C++ reference - https://www.cplusplus.com/reference/streambuf/streambuf/
- Custom Stream Buffers - http://videocortex.io/2017/custom-stream-buffers/