Making an std::istream produce one extra character pass its end

61 views Asked by At

I'm writing an adapter between different libraries in C++20. I reached a part where I get a std::istream from one library and pass it to the second library, which also takes std::istream. The problem is the second library requires a stream which ends with one specific character (let's say it is a full stop), and I'm 100% sure the first library gives me a stream without this character.

I's thinking about playing with stream buffers. I once made a custom stream buffer and it worked, but now I barely remember how I did it. Except this nothing comes to my mind.

In other words: how to force/modify/wrap-around/what-have-you an istream to produce exactly one extra character before it ends?

PS: I'm also constrained to use only C++'s Standard Library.

1

There are 1 answers

0
Felix.leg On

Thanks to @Sam Varshavchik I got an solution:

#include <istream>
#include <streambuf>

class ExtendedStreamBuf : public std::streambuf {
        
    private:
        std::streambuf * internal_stream;
        char extra_char;
        char single_buffer;
        bool stream_exhausted;
        
        // used for reading
        int_type underflow() override;
    
    public:
        ExtendedStreamBuf(std::streambuf * stream, char extra);
    
};

struct ExtendedStream : public std::istream {
    
    ExtendedStream(std::istream & stream, char extra);
    
};


ExtendedStream::ExtendedStream(std::istream & stream, char extra) :
    std::istream{new ExtendedStreamBuf(stream.rdbuf(), extra)}
{}

ExtendedStreamBuf::ExtendedStreamBuf(std::streambuf * stream, char extra) :
    internal_stream{stream}, extra_char{extra}, stream_exhausted{false}
{
    setg(&single_buffer, &single_buffer, &single_buffer);
}

std::streambuf::int_type ExtendedStreamBuf::underflow() {
    traits_type::int_type i;
    if( !stream_exhausted ) {
        i = internal_stream->sbumpc();
        if (!traits_type::eq_int_type(i, traits_type::eof())) {
            single_buffer = traits_type::to_char_type(i);
            setg(&single_buffer, &single_buffer, &single_buffer+1);
            return i;
        } else {
            stream_exhausted = true;
            single_buffer = extra_char;
            setg(&single_buffer, &single_buffer, &single_buffer+1);
            return traits_type::to_int_type( *gptr() );
        }
    } else {
        return traits_type::eof();
    }
}

Now I have to just create a new instance of ExtendedStream class and pass it to the second library.