Apache C++ Standard Library User's Guide

## 34.2 Copying and Assigning Stream Objects

Stream objects cannot simply be copied and assigned. Let us consider a practical example to see what this means. A program writes data to a file if a file name is specified on program call, or to the standard output stream `cout` if no file name is specified. You should write to one output stream in your program; this stream can be either a file stream or the standard output stream. The most obvious way to do this is to declare an output file stream object and assign it to `cout`, or to use `cout` directly. However, you can't do it this way:

    #include <iostream>                          // for cout, endl
    #include <fstream>                           // for ofstream

    int main(int argc, char argv[])
    {
      std::ofstream fil;
      if (argc > 1)
      {  fil.open(argv[1]);
        std::cout = fil;                            // can't do this !!!
      }
      // output to cout, for example
      std::cout << "Hello world!" << std::endl;
    }

This solution is bad for at least three reasons. First, the predefined stream objects `std::cin`, `std::cout`, `std::cerr`, and `std::clog` (and their wide character counterparts) have special properties and are treated differently from other streams. If you could reassign them, as done with `cout` in the example above, you might lose some of their special properties. Second, if allowed, assignment and copying of streams would be hazardous due to the possibility of the two copies sharing the same stream buffer. Which of the copies would be responsible for deleting the buffer object? For these reasons, the base class for iostreams has private assignment and copy constructors to prevent you from doing this.

> NOTE -- Stream objects must never be copied or assigned to each other.

### 34.2.1 Copying a Stream's Data Members

To achieve the equivalent effect of a copy, you might consider copying each data member individually. This can be done as follows:

    int main(int argc, char *argv[])
    {
      std::ofstream out;
      if (argc > 1)
        out.open(argv[1]);
      else {
        out.copyfmt(std::cout);                                  //1
        out.clear(std::cout.rdstate());                          //2
        out.basic_ios<char>::rdbuf(std::cout.rdbuf());           //3
      }
      // output to out, for example
      out << "Hello world!" << std::endl;
    }

`//1`

The `std::basic_ios<>::copyfmt()` member function copies all data from the standard output stream `cout` to the output file stream `out`, except the error state and the stream buffer. The function `std::basic_ios<>::exceptions()` allows you to copy the exception mask separately, as in `std::cout.exceptions(fil.exceptions())`, but you need not do this explicitly, since `copyfmt()` already copies the exception mask.

`//2` | Here the error state is copied.
`//3` | Here the stream buffer pointer is copied. Note the peculiar syntax used to call the overloaded member function `rdbuf()`. The qualification is necessary in order to resolve the call to the overload defined in `std::basic_ios<char>` rather than the one provided in `std::ofstream` (which takes no arguments).

Please note the little snag here. After the call to `rdbuf()`, the buffer is shared between the two streams, as shown in Figure 30:

#### Figure 30: Copying a stream's internal data results in a shared buffer

### 34.2.2 Sharing Stream Buffers Inadvertently

Whether or not you intend to share a stream buffer among streams depends on your application. In any case, it is important that you realize the stream buffer is shared after a call to `rdbuf()`; in other words, you must monitor the lifetime of the stream buffer object and make sure it exceeds the lifetime of the stream. In our little example above, we use the standard output stream's buffer. Since the standard streams are static objects, their stream buffers have longer lifetimes that most other objects, so we are safe. However, whenever you share a stream buffer among other stream objects, you must carefully consider the stream buffer's lifetime.

The example above has another disadvantage we haven't considered yet, as shown in the following code:

    int main(int argc, char *argv[])
    {
      std::ofstream out;

      if (argc > 1)
        out.open(argv[1]);
      else {
        out.copyfmt(std::cout);                                  //1
        out.clear(std::cout.rdstate());                          //2
        out.basic_ios<char>::rdbuf(std::cout.rdbuf());           //3
      }

      out << "Hello world!" << std::endl;

    }

`//1` | Copy the values of member variables (other than the streambuffer and the iostate) in `cout` to `out`.
`//2` | Set state flags for `out` to the current state of `std::cout`.
`//3` | Replace `out`'s streambuffer with `std::cout`'s streambuffer.

As we copy the standard output stream's entire internal data, we also copy its special behavior. For instance, the standard output stream is synchronized with the standard input stream. (See Chapter 35 for further details.) If our output file stream `out` is a copy of `std::cout`, it is forced to synchronize its output operations with all input operations from `std::cin`. This might not be desired, especially since synchronization is a time-consuming activity. Here is a more efficient approach using only the stream buffer of the standard output stream:

    int main(int argc, char *argv[])
    {
      std::filebuf* fb = new std::filebuf;                        //1

      std::ostream out((argc>1)?                                  //2
        fb->open(argv[1],
                 std::ios_base::out|std::ios_base::trunc):        //3
        std::cout.rdbuf());                                       //4
      if (out.rdbuf() != fb)
        delete fb;
      out << "Hello world!" << std::endl;
    }

`//1` | Instead of creating a file stream object, which already contains a file buffer object, we construct a separate file buffer object on the heap that we can hand over to an output stream object if needed. This way we can delete the file buffer object if not needed. In the original example, we constructed a file stream object with no chance of eliminating the file buffer object if not used.
`//2` | An output stream is constructed. The stream has either the standard output stream's buffer, or a file buffer connected to a file.
`//3` | If the program is provided with a file name, the file is opened and connected to the file buffer object. (Note that you must ensure that the lifetime of this stream buffer object exceeds the lifetime of the output stream that uses it.) The `std::filebuf::open()` member function returns a pointer to the file buffer object. This pointer is used to construct the output stream object.
`//4` | If no file name is provided, the standard output stream's buffer is used.

As in the original example, `out` inserts through the standard output stream's buffer, but lacks the special properties of a standard stream.

Here is an alternative solution that uses file descriptors, a nonstandard feature of this implementation of the standard iostreams.

> NOTE -- This feature was available in the traditional iostreams, but is not offered by the standard iostreams. This implementation of the standard iostreams retains the old feature for backward compatibility with the traditional iostreams, but it is nonstandard. Using it might make your application nonportable to other standard iostream libraries.

    int main(int argc, char *argv[])
    {
      std::ofstream out;
      if (argc > 1)
         out.open(argv[1]);                                         //1
      else
         out.rdbuf()->open(STDOUT_FILENO);                          //2
      out << "Hello world!" << std::endl;
    }

`//1` | If the program is provided with a file name, the file is opened and connected to the file buffer object.
`//2` | Otherwise, the output stream's file buffer is connected to the standard input stream `stdout` whose file descriptor is `STDOUT_FILENO` (a manifest constant typically `#defined` to `1` in the POSIX header `<unistd.h>`).

The effect is the same as in the previous solution, because the standard output stream `std::cout` is connected to the C standard file `stdout`. This is the simplest of all solutions, because it doesn't involve reassigning or sharing stream buffers. The output file stream's buffer is simply connected to the right file. However, this is a nonstandard and nonportable solution.

### 34.2.3 Using Pointers or References to Streams

If you do not want to deal with stream buffers at all, you can also use pointers to streams instead. Here is an example:

    int main(int argc, char *argv[])
    {
      std::ostream* fp;                                           //1
      if (argc > 1)
         fp = new std::ofstream(argv[1]);                         //2
      else
         fp = &std::cout                                          //3

      *fp << "Hello world!" << std::endl;                         //4
      if (fp != &std::cout)
         delete fp;
    }

`//1` | A pointer to an `std::ostream` is used. (Note that it cannot be a pointer to an `std::ofstream`, because the standard output stream `cout` is not a file stream, but a plain stream of type `std::ostream`.)
`//2` | A file stream for the named output file is created on the heap and assigned to the pointer, in case a file name is provided.
`//3` | Otherwise, a pointer to `std::cout` is used.
`//4` | Output is written through the pointer to either `std::cout` or the named output file.

Working with pointers has a drawback: you must create an output file stream object on the heap and, in principle, you must worry about deleting the object again, which might lead you into other dire straits.

In summary, creating a copy of a stream is not trivial and should only be done if you really need a copy of a stream object. In many cases, it is more appropriate to use references or pointers to stream objects instead, or to share a stream buffer between two streams.

> NOTE -- Never create a copy of a stream object when a reference or a pointer to the stream object would suffice, or when a shared stream buffer would solve the problem.