6
votes

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;
}
3
ostream is not copyable.Luchian Grigore
so how can I pass a generic reference to an output stream?ducin
@tkoomzaaskz: by using std::ostream&, specifically using the & after the type (as known as passing by reference).Thomas Matthews

3 Answers

15
votes

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.
4
votes

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.

0
votes

I would actually use an ostream instance in the module I might want to debug. Note that there is no default constructor for this type, you have to pass a pointer to a streambuffer, but that pointer can be null. Now, when/if you want to capture the module's output, you just attach a streambuffer to it using rdbuf(). This streambuffer can be std::cout's streambuffer, but it can also be a std::stringbuf or std::filebuf or some self-written one that automatically redirects the output to some window. One caveat: Output without streambuffer will set the failbit (or even badbit?) so you have to call clear() on the output stream after changing the streambuffer. Also note that you must manually manage the lifetime of this streambuffer and the streams referencing it, there is no ownership transfer and automatic cleanup involved.