I'm attempting to create a custom std::streambuf
which acts as a sub-stream to a parent stream. This is an adaptation of the implementation outlined in this SO thread answer.
In this example below I am attempting to simply read the first 5 characters "Hello,"
of the stream. However, when I call ifstream.read()
into the buffer
, the buffer is filled with "ello, "
, as though it is off by one.
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
ss << "Hello, World!";
substreambuf asd(ss.rdbuf(), 0, 5);
istream istream(&asd);
char buffer[6] = { '\0' };
istream.read(buffer, sizeof(buffer));
cout << buffer << endl; //prints "ello, "
}
I am new to streambuf
s and I feel like I'm missing something obvious here. Any help would be appreciated.
Here is the definition for substreambuf
:
#include <iostream>
#include <sstream>
namespace std
{
struct substreambuf : public streambuf
{
explicit substreambuf(streambuf* sbuf, streampos pos, streampos len) :
m_sbuf(sbuf),
m_pos(pos),
m_len(len),
m_read(0)
{
m_sbuf->pubseekpos(pos);
setbuf(nullptr, 0);
}
protected:
int underflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
return m_sbuf->sgetc();
}
int uflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
m_read += 1;
return m_sbuf->snextc();
}
streampos seekoff(streamoff off, ios_base::seekdir seekdir, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
if (seekdir == ios_base::beg)
{
off += m_pos;
}
else if (seekdir == ios_base::cur)
{
off += m_pos + m_read;
}
else if (seekdir == ios_base::end)
{
off += m_pos + m_len;
}
return m_sbuf->pubseekpos(off, openmode) - m_pos;
}
streampos seekpos(streampos streampos, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
streampos += m_pos;
if (streampos > m_pos + m_len)
{
return -1;
}
return m_sbuf->pubseekpos(streampos, openmode) - m_pos;
}
private:
streambuf* m_sbuf;
streampos m_pos;
streamsize m_len;
streampos m_read;
};
};
I thought this was strange when I first saw the code.
int uflow()
{
// ...
return m_sbuf->snextc();
}
Why is he returning the result of snextc()
? The policy defined for uflow()
is "return the next available character and advance the input stream by one character". If snextc()
is called, the input sequence will be advanced, and then it will return the next character. The result is that at least 1 character is skipped.
The correct method to call would be sbumpc()
because it will cache the next character first, advance the input stream, and then return it.
return m_sbuf->sbumpc();