3

I have a std::basic_streambuf subclass that causes all output to be written in uppercase, like this:

class upper_streambuf : public std::streambuf
{
    public:
        upper_streambuf(std::streambuf &real)
          : m_realBuffer(real)
        {
        }

    protected:
        virtual int overflow(int c)
        {
            int uc = std::toupper(c);
            m_realBuffer.sputc(uc);
            return uc;
        }

    private:
        std::streambuf &m_realBuffer;
};

I use it like this for example (which seems to work OK):

upper_streambuf buf(*std::cout.rdbuf());
std::ostream ucout(&buf);

ucout << "Hello, world!" << std::endl; // prints "HELLO, WORLD!"

What I want to achieve is the more-or-less the inverse, I want to read from a stream and have all input converted to lowercase. I have the following:

class lower_streambuf : public std::streambuf
{
    public:
        lower_streambuf(std::streambuf &real)
          : m_realBuffer(real)
        {
        }

    protected:
        virtual int underflow()
        {
            return std::tolower(m_realBuffer.sbumpc());
        }

    private:
        std::streambuf &m_realBuffer;
};

However, when I try and use this one like this:

lower_streambuf buf(*std::cin.rdbuf());
std::istream lcin(&buf);
std::string line;

std::getline(lcin, line);

The result is a segmentation fault. Am I overriding or calling the wrong functions? Please note I am somewhat of a novice in C++. Please also note that I understand I can read the input completely and simply convert it to lowercase once it has been read, but this is more for learning/academic purposes rather than anything practical.

dreamlax
  • 93,976
  • 29
  • 161
  • 209
  • Maybe I'm missing someting here, but why is it that you both inherit from std::streambuf, and contain one as a member variable. – Borgleader Jan 29 '14 at 02:13
  • @Borgleader It's perfectly fine. It's a wrapper around a streambuf object. – David G Jan 29 '14 at 02:14
  • To start with, [int underflow()](http://en.cppreference.com/w/cpp/io/basic_streambuf/underflow) takes no arguments... – Cubbi Jan 29 '14 at 02:27
  • @Cubbi: Oops, copy-paste error (I just copied the `upper_streambuf` implementation). Edited question, now that code matches my actual code. – dreamlax Jan 29 '14 at 02:33

1 Answers1

4

underflow() has post-conditions that you failed to establish: by the time you return from it, you need to have a get area in your streambuf that holds that character you're returning.

A single-character buffer is sufficient:

    protected:
        virtual int underflow()
        {
            ch = std::tolower(m_realBuffer.sbumpc());
  +         setg(&ch, &ch, &ch+1); 
            return ch;
        }
    private:
  +     char ch; // input buffer!
        std::streambuf &m_realBuffer;
};
Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • I *think* I understand... the "get" buffer is required because some read operations don't "consume" the character, is that right? – dreamlax Jan 29 '14 at 02:58
  • @dreamlax broken postcondition = anything can happen. Specifically, `getline` calls `sbumpc` (in the implementation I just looked at), which calls `uflow` when the get area is empty, which calls `underflow`, and if that doesn't return eof, it dereferences the get pointer and returns the char that's there. – Cubbi Jan 29 '14 at 03:09