问题
To understand how input streams work I designed 2 of the following classes:
#include <iostream>
class my_streambuf: public std::streambuf
{
private:
std::streambuf* buffer;
char ch;
protected:
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if (result != traits_type::eof())
{
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
public:
my_streambuf(std::streambuf* buffer) : buffer(buffer) {};
virtual ~my_streambuf() {};
};
class my_istream: public std::istream
{
public:
my_istream(std::istream& stream) : std::istream(new my_streambuf(stream.rdbuf())) {};
virtual ~my_istream()
{
delete rdbuf();
}
};
int main()
{
char s[32];
my_istream is(std::cin);
is >> s;
std::cout << s;
return 0;
}
Which work fine, until I change the logic of underflow
method. The primary goal is to save data in c-string valiable s
which differs from user-input. To make a simple test, I changed the underflow
method to be the following:
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if (result != traits_type::eof())
{
result = traits_type::to_int_type('+'); // <-- this was added
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
With the idea being to make the method return only +
symbols instead of user-input chars.
So for example if input is 123
, I expect +++
to be stored in variable s
.
And that does not work. Console hangs as if it is waiting more input. Only a certain amount of keypressing (or sending EOF) helps.
What am I missing here?
As was pointed out by @ferosekhanj, the problem was the missing newline, which was not returned by the modified version of underflow
to the caller. So in order for the code to work properly it has to be returned. This version of the method works fine.
virtual std::streambuf::int_type underflow()
{
std::streambuf::int_type result = buffer->sbumpc();
if ((result != traits_type::eof()) && !traits_type::eq(traits_type::to_char_type(result), '\n'))
{
result = traits_type::to_int_type('+');
ch = traits_type::to_char_type(result);
setg(&ch, &ch, &ch + 1);
}
return result;
}
回答1:
From my old C++ experience a stream buf is the underlying buffer for the stream. When the stream needs more data it calls underflow. Inside this method you are suppose to read from your source and setg. When the stream has data to be written back to the source it calls overflow. Inside this method you read from the stream,write back to your source and setp. For example if you are reading the data from a socket in your streambuf
socketbuf::int_type socketbuf::underflow(){
int bytesRead = 0;
try{
bytesRead = soc->read(inbuffer,BUFFER_SIZE-1,0);
if( bytesRead <= 0 ){
return traits_type::eof();
}
}catch(IOException ioe){
cout<<"Unable to read data"<<endl;
return traits_type::eof();
}
setg(inbuffer,inbuffer,inbuffer+bytesRead);
return traits_type::to_int_type(inbuffer[0]);
}
socketbuf::int_type socketbuf::overflow(socketbuf::int_type c){
int bytesWritten = 0;
try{
if(pptr() - pbase() > 0){
bytesWritten = soc->write(pbase(),(pptr() - pbase()),0);
if( bytesWritten <= 0 ) return traits_type::not_eof(c);
}
}catch(IOException ioe){
cout<<"Unable to write data"<<endl;
return traits_type::eof();
}
outbuffer[0] = traits_type::to_char_type(c);
setp(outbuffer,outbuffer+1,outbuffer+BUFFER_SIZE);
return traits_type::not_eof(c);
}
Now coming to your code, you added
result = traits_type::to_int_type('+'); // <-- this was added
A stream reads a string until it sees a LF(line feed). So when the LF character come you are over writing that with a '+' so the stream will wait (for LF) forever.By adding this check your code should do what you are expecting. output '+++' if you input 'abc'
if (result != 10)// <-- add this in addition
result = traits_type::to_int_type('+'); // <-- this was added
Hope it helps you.
来源:https://stackoverflow.com/questions/39017752/custom-input-stream-stream-buffer-and-underflow-method