Search code examples
c++ostream

Using std::ostream as a class member


I'm working on a simple progress indicator class, and I have a question about using a std::ostream object as a class member. The following example compiles and runs correctly on OS X and Linux.

#include <iostream>
#include <string>

struct ProgressIndicator {

public:
    ProgressIndicator(unsigned int update_interval, std::string message,
                      std::ostream& outstream = std::cerr)
        : update_interval(update_interval), message(message),
          stream(outstream.rdbuf()), _counter(0), _interval(update_interval)
    {
    }

    void increment(unsigned int by = 1)
    {
        _counter += by;
        if(_counter >= _interval) {
            stream << message << _counter << std::endl;
            _interval += update_interval;
        }
    }

    unsigned int get_count()
    {
        return _counter;
    }

protected:
    unsigned int update_interval;
    std::string message;
    std::ostream stream;

private:
    unsigned long _counter;
    unsigned long _interval;

};

int main()
{
    ProgressIndicator p1(5, "progress <stdout> ", std::cout);
    for(int i = 0; i < 15; i++) {
        p1.increment();
    }

    ProgressIndicator p2(5, "progress <stderr> ", std::cerr);
    for(int i = 0; i < 15; i++) {
        p2.increment();
    }
    return 0;
}

I understand that std::ostream objects cannot be copied, and must be passed by reference. But I don't understand why initializing with stream(outstream) doesn't work, and why I had to resort to the rdbuf() hack. Is there no "better", more idiomatic way to do this?


Solution

  • You are still copying the std::ostream. Even though the constructor isn't copying the parameter, it still needs to copy the object into ProgressIndicator::stream somehow.

    One way to solve your problem would be to store a reference to the stream, though this only works if you know the stream object will outlast your class instance:

    struct ProgressIndicator {
        ProgressIndicator(std::ostream& outstream = std::cerr /* ... */)
          : stream(outstream) /* ... */ {}
        // ...
    
    protected:
        std::ostream& stream;
        // ...
    };