0

I have an instance of a class which implements a writing interface such as this one:

struct Writer {
    virtual size_t write(const char* buffer, size_t size, size_t offset) = 0;
};

I also have a function that takes the std::ostream& as an argument and writes to it. Like so:

void foo(std::ostream& os) {
    os << "hello world"; // Say, I don't control this code
}

What is the simplest way to make foo write into my instance of the Writer interface?

Bonus points for a Reader interface and std::istream.

EDIT:

Maybe I should also mention, I must assume that foo writes a huge amount of data to the std::ostream, thus a solution where foo first writes into std::stringstream which I then write into the Writer won't work.

Peter Jankuliak
  • 3,464
  • 1
  • 29
  • 40
  • Have foo take a `Writer&`? Or have `Writer` operate on `std::ostream&`? – Stephen Newell Feb 26 '21 at 16:35
  • 4
    You're going to need to create your own class that derives from `ostream` and have that interface with `Writer`. The same will apply to the `istream` case. – NathanOliver Feb 26 '21 at 16:37
  • @StephenNewell no, in this scenario I can't modify `foo` nor `Writer`, I can only create some kind of a wrapper that either is used by `std::ostream` itself or derives from it. – Peter Jankuliak Feb 26 '21 at 16:40
  • Maybe [this question](https://stackoverflow.com/questions/772355/how-to-inherit-from-stdostream) can help. – super Feb 26 '21 at 16:48
  • 2
    I would write a custom [`std::basic_streambuf`](https://en.cppreference.com/w/cpp/io/basic_streambuf) class that outputs to a `Writer`, then instantiate a standard `std::ostream` object passing my `streambuf` to its constructor. – Remy Lebeau Feb 26 '21 at 17:45
  • Is this a duplicate of https://stackoverflow.com/questions/13703823/a-custom-ostream ? – KamilCuk Feb 26 '21 at 23:46
  • @KamilCuk Sorry for the late reply: I don't think this is a duplicate, many of the questions "out there" (such as the one you linked) ask a generic question on how to implement an std::ostream (or std::istream). But I feel there is a lack of good answers. Thus I tried to narrow the scope to hopefully be useful for others as this ought be something people shouldn't need to suffer through each time. – Peter Jankuliak Mar 03 '21 at 13:47

1 Answers1

2

Working using code from A custom ostream, I was able to write:

#include <iostream>
#include <sstream>

struct Writer {
    virtual size_t write(const char* buffer, size_t size, size_t offset) = 0;
};
Writer& getWriter();
void foo(std::ostream& os);

struct WriteToWriter : public std::stringbuf {
    Writer& w;
    std::size_t offset{0};
    WriteToWriter(Writer& w) : w(w) {}
    virtual std::streamsize xsputn(const char_type* s, std::streamsize n) override {        
        const size_t r = w.write(s, n, offset);
        offset += r;
        return r;
    }
    virtual int_type overflow(int_type c) override {
        if (c == EOF) return EOF;
        char tmp = c;
        const size_t r = w.write(&tmp, 1, offset);
        offset += r;
        return r == 1 ? c : EOF ;
    }
};

int main()
{
    WriteToWriter wtw(getWriter());
    std::ostream out(&wtw);
    foo(out);
}

that looks like it should write directly to Writer::write

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • I have two issues with this answer: (1) this doesn't need to inherit from std::stringbuf, inheriting from std::streambuf should suffice. (2) Does `overflow` actually need to be implemented. The [xsputn documentation](https://en.cppreference.com/w/cpp/io/basic_streambuf/sputn) says that *xsputn* may call it, but the version you wrote doesn't. I did some testing and it doesn't seem to be called. – Peter Jankuliak Mar 03 '21 at 14:27
  • 1. Sure. 2. Yes, there's `sputc`. – KamilCuk Mar 04 '21 at 03:02