How to make a simple C++ program in which std::cout is not flushed
Asked Answered
A

5

13

In an effort to better understand buffered streams in C++, I would like to write a simple program in which the std::cout buffer is NOT flushed before termination. Since I have read that std::cout is flushed on normal termination, I tried throwing a runtime error. I also avoided using std::endl, as I understand that forces a flush. First attempt:

//file noflush.cpp
#include <iostream>

int main() {
    std::cout << "Don't write me to the console!";
    throw 0;
}

Compile with g++, call from terminal:

$ ./noflush
libc++abi.dylib: terminating with uncaught exception of type int
Don't write me to the console!Abort trap: 6

Even when I force a runtime error, it seems the buffer still gets flushed on termination. Is it possible to "strand" some data in the buffer, leaving it unwritten to the device?

Alguire answered 1/6, 2016 at 7:53 Comment(1)
Just to say that even when aborting with std::abort() the buffer is flushed, (using Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) with LLVM libc++), though according to cppreference it is implementation defined whether open resources such as files are closed (upon calling std::abort()).Bencion
M
10

This is not standard c++, but in POSIX, you can send a "kill" signal to kill the running process. This will stop the execution without cleanup such as flushing buffers.

Edit: I realized that signals are not only POSIX but actually part of C standard library (and included in the C++ standard library).

#include <csignal>
// ...
std::cout << "Don't write me to the console!";
std::raise(SIGKILL);
Matrimony answered 1/6, 2016 at 8:22 Comment(5)
How about dereference null pointer to induce null pointer exception / runtime seg faul? That might be a good cross-platform way to kill a process.Writer
@Writer dereferencing a null pointer has undefined behaviour, so it's not guaranteed to kill the process and isn't portable technically... In practice though, crash on null pointer dereference might be more widely "supported" than POSIX. That's basically same suggestion as Caduchon's answer, except the UB from null pointer dereference is probably more consistent than UB from dereferencing and deleting an uninitialized pointer.Matrimony
@Writer Crashing on dereferencing a null pointer is not guaranteed.Matchbox
@Matchbox I suspect the non-flushing of buffers on SIGKILL is also not guaranteed. Or even the existence of SIGKILL.Liberec
@immibis It is guaranteed by the semantics of SIGKILL. Programs cannot handle SIGKILL and so cannot run cleanup code in response to it.Chieftain
B
9

As far as I can tell, there is no standard compliant and clean way to avoid std::cout to flush() before program termination (but, of course, you can use unclean methods, e.g. raising a signal either directly or indirectly). According to cppreference, the actual buffer type controlled by std::cout is implementation defined but derived from std::streambuf, which does not appear to allow public access in a way that would emulate silent swallowing of the buffer.

Further, as I noted in a comment, even abnormal program termination (via std::terminate() or std::abort() may or may not close open resources, so this is again implementation defined.

Bencion answered 1/6, 2016 at 8:30 Comment(1)
The correct quote is "The global objects std::cout [...] control output to a stream buffer of implementation-defined type [...]" (The buffer and not the stream object is implementation defined)Claraclarabella
D
4

With the following example, I can create the behaviour you want with gcc 4.8.3 :

#include <iostream>
#include <vector>

int main()
{
    std::string str;
    for(unsigned long int i = 0; i < 10000; ++i)
        str += "Hello ! ";
    str += "END";
    std::cout << str;

    std::vector<double>* p;
    p->push_back(1.0);
    delete p;

    std::cout << "STILL ALIVE !" << std::endl;

    return 0;
}

Then, the output is :

Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! Hello ! [...] Hello ! Segmentation fault

We can see than END is not printed before the segmentation fault.

Decasyllabic answered 1/6, 2016 at 8:21 Comment(8)
Uhmm... does the example work even with 10 instead of 10000 in the loop?Lippe
When I tried with 10, the segmentation fault was immediate. Thus yes, it also works, but more fuzzily.Decasyllabic
the initial code does NOT show the behaviour OP is looking for, because the program crash before even putting "STILL ALIVE !" in the buffer, while with 10 instead of 10000 it crashes without displaying anything, while the Hello! ARE in the buffer : this is the bahaviour expectedLongdistance
No, the end of the string is "END". It's not printed. Then the string is partially printed. The buffer is not flusshed correclty. I don't understand you put -1 for that !Decasyllabic
My bad, i didn't look the program answer till this end, still, why did you did a loop, and why the std::cout << "STILL ALIVE !" << std::endl; they both draw away from the point. A simple cout before the intentionnal crash would have been clearer, and btw, don't assume everybody on the internet is an asshole, i just wanted you to make your answer a little more simple and to the point, I didn't -1 you, DON'T assume things you just don't know about what others did.Longdistance
The loop is for the creation of a big string : better to see buffer problems occur. The "END" for simply check all the string is printed. "STILL ALIVE" because a segmentation fault is not always throwed (as example, if you access to your own memory, typically a growing buffer). Stay calm ! The -1 and your comment happened in the same time, and comment a -1 is a good practice. It was normal I assumed it was you...Decasyllabic
@Decasyllabic still, it's polite to give the commenters the benefit of doubt. They're trying to help after all and unfortunately this site has plenty of downvote ninjas who don't bother justifying their votes. P.S. I'm sorry for going totally off topic.Matrimony
Apologizes if I seem unpolite. I don't quite agree with The loop is for the creation of a big string : better to see buffer problems occur, because the loop is more than anything showing that the buffer regularly get flushed when being filled, which is not really the question, but related and interesting I admit. For the fact that segfault don't always happens, it's an undefined behaviour not at all linked with the problem, so you could just set explicity the pointer to not accessible memoryLongdistance
C
0

If I get you right, you want to capture or ignore the output to std::cout:

#include <iostream>
#include <sstream>
int main()
{
    // Capture the output to `std::cout`
    {
        std::cout << "[Capture Output]" << std::endl;
        std::stringstream cpature;
        auto restore = std::cout.rdbuf(cpature.rdbuf());

        std::cout << "... captured output ..." << std::endl;

        std::cout.rdbuf(restore);
        std::cout << "[Enable Output]" << std::endl;

        // Display the cpatured output.
        std::cout << cpature.rdbuf();
    }
    std::cout << std::endl;

    // Even more drasticly: Ignore the output to `std::cout`
    {
        std::cout << "[Ignore Output]" << std::endl;
        auto restore = std::cout.rdbuf(nullptr);

        std::cout << "... ignored output ..." << std::endl;

        std::cout.rdbuf(restore);
        std::cout << "[Enable Output]" << std::endl;
    }

    std::cout << "[End]\n";
}
Claraclarabella answered 1/6, 2016 at 20:21 Comment(0)
E
0
#include <iostream>
#include <sstream>
#include <vector>
int main()
{
    std::stringstream cpature;
    auto restore = std::cout.rdbuf(cpature.rdbuf());    
    std::cout.rdbuf(restore);
    for(unsigned long int i = 0; i < 10000; ++i)
        std::cout <<"Hello ! "  << std::endl;
    std::cout << "END"  << std::endl;

    std::cout << cpature.rdbuf();
    std::vector<double> *p;
    p->push_back(1.0);
    delete p;
    std::cout << "STILL ALIVE !" << std::endl;
}
Esophagitis answered 12/6, 2016 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.