Output stream as class member

10,589

Solution 1

You've got two options here:

  • Use references, or
  • Use pointers

You can't use normal instances because ostream is non-copyable.

Using references (direct reference to an already-instantiated ostream)

class PVirtualMachine {
  private:
    std::ostream & output;
    [...]
  public:
    PVirtualMachine(std::ostream &);  // Reference must be initialized on construction.
    [...]
};

Advantages:

  • No pointer syntax.
  • Should always refer to a valid instance of std::ostream, as long as the original variable is not deleted.

Disadvantages:

  • The PVirtualMachine class must be constructed with the output reference in the initialization list, otherwise it will not compile.
  • Cannot change the reference once it has been initialized.
  • Cannot work with move-assignment operators (i.e. operator=(PVirtualMachine &&))

Using pointers (optional reference to object)

class PVirtualMachine {
  private:
    std::ostream * output;
    [...]
  public:
    void setOutput(std::ostream *);
    [...]
};

Advantages:

  • Can be instantiated as a null pointer.
  • Can be passed around easily.
  • Can be updated to point to a new std::ostream instance.
  • Can be created internally or externally to the PVirtualMachine instance.
  • Works with move-assignment operator.

Disadvantages:

  • Pointer syntax.
  • Must check for null references when accessing the ostream and/or in the constructor.

Solution 2

You can use a reference to a std::ostream instead, this would support any kind of output stream, e.g. stdout, file, etc. This is fine as long as you only want to use one single stream, and the stream doesn't get destroyed:

class PVirtualMachine {
  private:
    std::ostream & output;
    [...]
  public:
    PVirtualMachine(std::ostream & os = std::cout): output(os) { }
    // void setOutput(std::ostream & os) { output = os; } // can't change the reference
    [...]
};

If you want this class to share the stream (therefore keeping it alive for this class's lifetime), use a std::shared_ptr<std::ostream> instead of a reference.

Share:
10,589
ducin
Author by

ducin

py / js / java enthusiast. Curious of many technologies.

Updated on August 13, 2022

Comments

  • ducin
    ducin over 1 year

    I have a c++ library that provides an object with complicated logic. During data processing, this object outputs lots of things to std::cout (this is hardcoded now). I would like the processing output not to go to standard output but to a custm widget (some text displaying). I tried to create a std::ostream class member, set it with a parameter (std::cout for console application and some other ostream handled inside GUI application). But the compiler throws me following errors:

    [ 14%] Building CXX object src/core/CMakeFiles/PietCore.dir/pvirtualmachine.cpp.o
    /usr/include/c++/4.6/ostream: In constructor ‘PVirtualMachine::PVirtualMachine(QString)’:                                                                        
    /usr/include/c++/4.6/ostream:363:7: error: ‘std::basic_ostream::basic_ostream() [with _CharT = char, _Traits = std::char_traits]’ is protected
    /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:33:50: error: within this context
    In file included from /usr/include/c++/4.6/ios:45:0,
                     from /usr/include/c++/4.6/ostream:40,
                     from /usr/include/c++/4.6/iterator:64,
                     from /usr/include/qt4/QtCore/qlist.h:50,
                     from /usr/include/qt4/QtCore/qvector.h:48,
                     from /usr/include/qt4/QtGui/qpolygon.h:45,
                     from /usr/include/qt4/QtGui/qmatrix.h:45,
                     from /usr/include/qt4/QtGui/qtransform.h:44,
                     from /usr/include/qt4/QtGui/qimage.h:45,
                     from /usr/include/qt4/QtGui/QImage:1,
                     from /home/tomasz/Development/C++/piet/src/core/pcodepointer.h:17,
                     from /home/tomasz/Development/C++/piet/src/core/pblockmanager.h:9,
                     from /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.h:10,
                     from /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:4:
    /usr/include/c++/4.6/bits/ios_base.h: In member function ‘std::basic_ios& std::basic_ios::operator=(const std::basic_ios&)’:
    /usr/include/c++/4.6/bits/ios_base.h:791:5: error: ‘std::ios_base& std::ios_base::operator=(const std::ios_base&)’ is private
    /usr/include/c++/4.6/bits/basic_ios.h:64:11: error: within this context
    In file included from /usr/include/c++/4.6/iterator:64:0,
                     from /usr/include/qt4/QtCore/qlist.h:50,
                     from /usr/include/qt4/QtCore/qvector.h:48,
                     from /usr/include/qt4/QtGui/qpolygon.h:45,
                     from /usr/include/qt4/QtGui/qmatrix.h:45,
                     from /usr/include/qt4/QtGui/qtransform.h:44,
                     from /usr/include/qt4/QtGui/qimage.h:45,
                     from /usr/include/qt4/QtGui/QImage:1,
                     from /home/tomasz/Development/C++/piet/src/core/pcodepointer.h:17,
                     from /home/tomasz/Development/C++/piet/src/core/pblockmanager.h:9,
                     from /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.h:10,
                     from /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:4:
    /usr/include/c++/4.6/ostream: In member function ‘std::basic_ostream& std::basic_ostream::operator=(const std::basic_ostream&)’:
    /usr/include/c++/4.6/ostream:57:11: note: synthesized method ‘std::basic_ios& std::basic_ios::operator=(const std::basic_ios&)’ first required here 
    /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp: In member function ‘void PVirtualMachine::setOutput(std::ostream)’:
    /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.cpp:216:11: note: synthesized method ‘std::basic_ostream& std::basic_ostream::operator=(const std::basic_ostream&)’ first required here
    

    I'd be glad if someone pointed me out what is wrong, because I've got no idea...

    My code looks like this:

    • .h file
    class PVirtualMachine {
      private:
        std::ostream output;
        [...]
      public:
        void setOutput(std::ostream);
        [...]
    };
    
    • .cpp file
    void PVirtualMachine::setOutput(std::ostream os)
    {
      output = os;
    }
    
  • ducin
    ducin over 11 years
    /home/tomasz/Development/C++/piet/src/core/pvirtualmachine.c‌​pp:33:1: error: uninitialized reference member ‘PVirtualMachine::output’ [-fpermissive] - so I have to pass it in constructor's initializing list?
  • WhozCraig
    WhozCraig over 11 years
    Sure thing. I'd probably use a std::reference_wrapper<> and bind it to the given stream reference if it was absolutely mandatory we take a reference and the lifetime of the reference outside the object instance is known to exceed the object itself (like cout likely will, for example, etc). but the concept is the same regardless.
  • WhozCraig
    WhozCraig over 11 years
    The second disadvantage for references should be reflected in the code preceding it. I.e. there should be no setOutput() member, instead opting for a construct initializer-list.
  • Karl Nicoll
    Karl Nicoll over 11 years
    Well you can still set the output later on, but yes, it does need to be constructed initially.
  • WhozCraig
    WhozCraig over 11 years
    Once it is construct-initialized (which it must be), you cannot change it. references are initialize-only. Thus the need for setOutput() goes out the window in the fundamental case.
  • Karl Nicoll
    Karl Nicoll over 11 years
    @WhozCraig Really? Interesting. I always thought that was possible. I guess I've never tried it! I'll update my answer.
  • WhozCraig
    WhozCraig over 11 years
    Yeah, no dice. You can play the game with things like std::reference_wrapper<> if you absolutely had to take a reference, but for the case as you presented it, it is construct-init-only.
  • 0xcaff
    0xcaff about 7 years
    In your answer you say: Reference can become invalid if the original instance goes out of scope. Don't references keep the instance around even if the original goes out of scope? Where can I find more information about references and when they become invalid?
  • Karl Nicoll
    Karl Nicoll about 7 years
    @caffinatedmonkey - Variables are not reference counted in C++, so the program does not know how many references point to a variable. When an object instance goes out of scope, all references to it will point to invalid memory. If you need reference counting, you should look at std::shared_ptr. For information on how references are used, check out this article.