What can go wrong if cout.rdbuf() is used to switch buffer and never set it back?
Asked Answered
G

3

8

The author presented this code under the title A bus error on my platform

#include <fstream>
#include <iostream>

int main()
{
    std::ofstream log("oops.log");
    std::cout.rdbuf(log.rdbuf());
    std::cout << "Oops!\n";
    return 0;
}

The string "Oops!\n" is printed to the file "oops.log". The code doesn't restore cout's streambuf, but VS2010 didn't report a runtime error.

Garnett answered 13/2, 2013 at 18:17 Comment(0)
S
10

Since log and std::cout share a buffer, that buffer will probably be freed twice (once when log goes out of scope, then once more when the program terminates).

This results in undefined behavior, so it's hard to tell the exact reason why it triggers a bus error on his machine but silently fails on yours.

Surgeonfish answered 13/2, 2013 at 18:21 Comment(2)
Any way to get around this?Toro
@Caesar, yes, not sharing stream buffers in the first place. I wonder what the original author tried to achieve by doing that.Loewi
J
5

Since the other answers don't mention what to do about this I'll provide that here. You need to save and restore the buffer that cout is supposed to be managing. For example:

#include <fstream>
#include <iostream>

// RAII method of restoring a buffer
struct buffer_restorer {
    std::ios &m_s;
    std::streambuf *m_buf;

    buffer_restorer(std::ios &s, std::streambuf *buf) : m_s(s), m_buf(buf) {}
    ~buffer_restorer() { m_s.rdbuf(m_buf); }
};

int main()
{
    std::ofstream log("oops.log");
    buffer_restorer r(std::cout, std::cout.rdbuf(log.rdbuf()));
    std::cout << "Oops!\n";
    return 0;
}

Now when cout's buffer is replaced before cout is destroyed at the end of the program, so when cout destroys its buffer the correct thing happens.


For simply redirecting standard io generally the environment already has the ability to do that for you (e.g., io redirection in the shell). Rather than the above code I'd probably simply run the program as:

yourprogram > oops.log

Also one thing to remember is that std::cout is a global variable with all the same downsides as other global variables. Instead of modifying it or even using it you may prefer to use the usual techniques to avoid global variables all together. For example you might pass a std::ostream &log_output parameter around and use that instead of having code use cout directly.

Jinja answered 13/2, 2013 at 19:7 Comment(0)
H
3

Your program has Undefined Behavior.

The destructor of the global cout object will delete the stream buffer when going out of scope, and the same is true of log, which also owns that very same stream buffer. Thus, you are deleting the same object twice.

When a program has Undefined Behavior, anything could happen, from formatting your hard drive to terminating without any error.

On my platform, for instance, the program enters an infinite loop after returning from main().

Hypochondriasis answered 13/2, 2013 at 18:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.