Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom input stream. Stream buffer and underflow method

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;
}
like image 833
HighPredator Avatar asked Mar 12 '23 09:03

HighPredator


1 Answers

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.

like image 119
ferosekhanj Avatar answered Mar 15 '23 23:03

ferosekhanj