0

Following from Get an istream from a char*, I'm trying to write a mock stream container for some tests which looks as follows:

struct mock_membuf : std::streambuf
{
    mock_membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};

struct MockStreamContainer{
    explicit MockStreamContainer(char* buffer, int offset, int nbytes): m_sbuf(buffer + offset, buffer + offset + nbytes), m_body(&m_sbuf), m_size(nbytes) {}

    std::istream& Body() const {
        return m_body;
    }

    int Size() const {
        return m_size;
    }

    mock_membuf m_sbuf; // same as membuf from the question referenced
    std::istream& m_body;
    int64_t m_size;
};

and will be used as follows:

int main()
{
    char buffer[] = "I'm a buffer with embedded nulls\0and line\n feeds";

    auto get_stream = [&buffer](int offset, int nbytes) {
        return MockStreamContainer(buffer, offset, nbytes);
    };
    std::string line;
    auto r = get_stream(5, 10);
    std::istream& in = r.Body();
    while (std::getline(in, line)) {
        std::cout << "line: " << line << "\n";
    }
    return 0;
}

The above code is just something I tried(here is a link) and is error ridden - Any suggestions as to how this can be correctly and efficiently implemented?

P.S. As requested the above code throws the following compilation error currently:

main.cpp: In constructor 'MockStreamContainer::MockStreamContainer(char*, int, int)':

main.cpp:17:131: error: invalid initialization of non-const reference of type 'std::istream&' {aka 'std::basic_istream<char>&'} from an rvalue of type 'mock_membuf*'

     explicit MockStreamContainer(char* buffer, int offset, int nbytes): m_sbuf(buffer + offset, buffer + offset + nbytes), m_body(&m_sbuf), m_size(nbytes) {}

Edit: Thanks to @Martin York's answer, I was able to fix the problem by making a minor change: convert m_body to a pointer instead of a reference.

tangy
  • 3,056
  • 2
  • 25
  • 42

1 Answers1

1

Your member variable m_body is a reference:

std::istream&    m_body;
            ^     reference

So you are trying to initialize this reference by creating a temporary in the constructor.

explicit MockStreamContainer(char* buffer, int offset, int nbytes):
    m_sbuf(buffer + offset, buffer + offset + nbytes),
    m_body(&m_sbuf),    // Here you are passing a pointer to `std::streambuf`
                        // This is not an `std::istream` so the compiler
                        // is trying to create one using the single argument
                        // constructor.
                        //
                        // That worked. So you have a temporary `std::istream` object
                        //
                        // You can not bind a temporary object to a non const reference
                        // hence the compiler error.
    m_size(nbytes)
{}

I would do this:

#include <iostream>

struct mock_membuf : public std::streambuf
{
    mock_membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};

struct mock_stream: public std::istream
{
    mock_membuf     streamBuffer;
    public:
        mock_stream(char* buffer, int offset, int nbytes)
            : std::istream(nullptr)
            , streamBuffer(buffer + offset, buffer + offset + nbytes)
        {
            rdbuf(&streamBuffer);
        }
};

int main()
{
    char buffer[] = "I'm a buffer with embedded nulls\0and line\n feeds";

    std::string line;
    mock_stream in(buffer, 5, 10);
    while (std::getline(in, line)) {
        std::cout << "line: " << line << "\n";
    }
    return 0;
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thanks for pointing out the problem. What would be the correct way to go about setting up this container then? – tangy Nov 19 '18 at 20:26
  • Why are you using a reference in the first place? – Martin York Nov 19 '18 at 20:31
  • Since the copy constructor is deleted for an input stream, I believed that I will have to work with references to avoid problems when passing it around/returning it from functions. I'm sure that I'm just being silly and there's a better way to deal with this. – tangy Nov 19 '18 at 20:36
  • Actually, based on your suggestion I was able to fix it by making the m_body a pointer. Thanks! – tangy Nov 19 '18 at 20:43
  • Why not create a MockStream type (rather than a stream holder). – Martin York Nov 19 '18 at 20:43
  • The short answer is that I'm trying to mock something like this: https://github.com/aws/aws-sdk-cpp/blob/e9d19c7531d4154c88e3313e6a51ddae059c1651/aws-cpp-sdk-s3/include/aws/s3/model/GetObjectResult.h#L38 – tangy Nov 19 '18 at 20:45