8

While trying to figure out how to answer https://stackoverflow.com/questions/33601384/what-is-the-file-descriptor-of-linuxs-environments-standard-logging-stream, I noticed a link to an answer to a related SO post. I tried the code in the above linked answer with g++ 4.8.4 and got segmentation error before the program terminated.

Here's the program:

#include <iostream>
#include <fstream>

int main()
{
   std::ofstream of("cout.txt");
   std::cout.rdbuf(of.rdbuf());
   std::cout << "test. test. test." << std::endl;
   return 0;
}

The command to build the program:

g++ -Wall -std=c++11  -g   socc.cc   -o socc

Output from gdb:

GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from socc...done.
(gdb) run
Starting program: /.........../socc (removed some text here)
Traceback (most recent call last):
  File "/usr/share/gdb/auto-load/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19-gdb.py", line 63, in <module>
    from libstdcxx.v6.printers import register_libstdcxx_printers
ImportError: No module named 'libstdcxx'

Program received signal SIGSEGV, Segmentation fault.
0x000000000040073c in ?? ()
(gdb) bt
#0  0x000000000040073c in ?? ()
#1  0x0000000000000000 in ?? ()
(gdb) 

I updated the program to keep the old rdbuf of cout and reset it before the end of the program.

#include <iostream>
#include <fstream>

int main()
{
   std::ofstream of("cout.txt");
   auto cout_buff = std::cout.rdbuf();
   std::cout.rdbuf(of.rdbuf());
   std::cout << "test. test. test." << std::endl;
   std::cout.rdbuf(cout_buff);
   return 0;
}

With this change, the program ran without any problem.

I experimented similarly with cerr and clog with identical results.

That leads me to the questions:

Is it always necessary to reset rdbuf of cout, cerr, and clog if they have been changed to be redirected to a file?

If not, is this a g++ defect?

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270

1 Answers1

11

Citing from The C++ Standard Library - A tutorial and reference 2nd edition by Nicolai Josuttis,

Ch. 15.12.13, Redirecting Standard Streams pp. 822

...

std::cout.rdbuf (file.rdbuf());

Caution! The object file is local and is destroyed at the end of the block. This also destroys the corresponding stream buffer. This differs from the “normal” streams because file streams allocate their stream buffer objects at construction time and destroy them on destruction. Thus, in this example, cout can no longer be used for writing. In fact, it cannot even be destroyed safely at program termination. Thus, the old buffer should always be saved and restored later!

So the answer seems to be Yes, and even though I don't have a standard quote, the author above is quite an expert.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 1
    You don't necessarily need to swap it back; the problem is the lifetime of the `basic_filebuf`, which must outlast `cout` if you don't change the buffer to something else. For instance, you can do `std::cout.rdbuf(nullptr);` at the end instead of saving and restoring the original buffer. – T.C. Nov 09 '15 at 04:44
  • I noticed from the standard: *~ios_base(): The destructor does not destroy rdbuf()*. Looks like either `~basic_fstream()`, or `~basic_ofstream()` destroy the corresponding `rdbuf()`. – R Sahu Nov 09 '15 at 04:44
  • @T.C. Yes indeed, otherwise the destructor probably attempts a double deletion. – vsoftco Nov 09 '15 at 04:46
  • 4
    @vsoftco No, neither `basic_ios` nor `basic_ostream`'s destructor touches `rdbuf()`. The issue here is that `ios_base::Init::~Init()` flushes the streams, which is bad for obvious reasons if the stream buffer is gone. – T.C. Nov 09 '15 at 04:51