Streams and terminals!

In one of his answers @Peter87 wrote:
Streams are often "buffered", meaning that instead of writing the output to the destination straight away they often store it in a buffer so that they can write more at once later. This is often better for performance and for the terminal it can also help reducing flickering (partial output being displayed).


I read so many about flush, but now I got it, so thanks @Peter87.

Tho, this is what goes in my mind when I try to visualize it.

If I write

cout << "Hello" ;

The letters H, e, l, l and o will be buffered in the stream, then outputted to destination(The terminal)

instead of outputting:
H
He
Hel
Hell
Hello
to the terminal, and before each of the line above is written(e.i Hel), It needs to erase the one before(e.i He), which may result to flickering .

Am I right here?
Last edited on
Now you make assumptions about how a "terminal emulator" produces (and modifies) an image. That is separate from the "text content" that we do send to it.
One problem is that partial output gets displayed, which could look ugly.

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <chrono>
#include <thread>

std::string get_name()
{
	std::this_thread::sleep_for(std::chrono::milliseconds(500)); 
	return "Ninja";
}

std::string get_place()
{
	std::this_thread::sleep_for(std::chrono::milliseconds(500)); 
	return "Paris";
}

int main()
{
	std::cerr << "Hello " << get_name() << "! Nice meeting you here in " << get_place() << ".\n";
	//   ^^^^
	//   Replace with cout and see the difference!
}

Here I have used cerr because it is unbuffered (output gets flushed right away). Sometimes functions take a little while to complete so to simulate that I added a "sleep" in get_name() and get_place().

If you run this you will see the output appear in steps:
"Hello "
"Hello Ninja! Nice meeting you here in "
"Hello Ninja! Nice meeting you here in Paris."

If you change cerr to cout (which is buffered) you'll see everything gets outputted at the same time, at the end, which looks much nicer.


Another problem is if you draw something on top of something else. This can be done more nicely using third-party libraries (e.g. with something like ncurses) but I have tried to hack something together to demonstrate the problem using only standard C++.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <chrono>
#include <thread>

int main()
{
	std::string animation[] = 
	{
		" o-o>      M   ",
		"  o-o>     M   ",
		"   o-o>    M   ",
		"    o-o>   M   ",
		"     o-o>  M   ",
		"      o-o> M   ",
		"       o-o>M   ",
		"        ****   ",
		"       *BANG*  ",
		"      **BANG** ",
		"       *BANG*  ",
		"        ****   ",
		"         **    ",
		"               ",
		"      THE END  ",
	};
	
	for (const std::string& frame : animation)
	{
		// return to start of line 
		std::cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
		
		// display frame
		for (char ch : frame)
		{
			std::cout << ch;
			std::cout << std::flush; // remove this line and see the difference!
			std::this_thread::sleep_for(std::chrono::milliseconds(30));
		}
		
		std::cout << std::flush;

		// Change this number to control the speed of the animation.
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}

This is a small program that shows a simple animation of a car crashing.

I flush after each character and to make the problem more obvious I also "sleep" for 20 ms.
The problem is that in the process of drawing one frame on top of another you see two partial frames. This could be even more disturbing when the frames are more different.
The caret might make it look extra ugly, but there is no way hide it using only standard C++.

Remove the flush on line 36 and you'll see that the animation runs more smoothly.


A perhaps more common situation, where it appears like you're "overwriting" but you're not, is when outputting a fixed number of lines repeatedly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <chrono>
#include <thread>

int main()
{
	// Avoid automatic flushing at the end of lines
	// (You might not notice the difference since the 
	// lines are outputted so quickly after each other)
	std::ios::sync_with_stdio(false);
	
	int altitude = 2000;
	int time_since_takeoff = 20;
	
	while (true)
	{
		altitude += rand() % 21 - 10;
		++time_since_takeoff;
		
		std::cout << "\n\n";
		std::cout << "Aircraft: Airbus A380\n";
		std::cout << "Passangers : 242\n";
		std::cout << "Altitude: " << altitude << " m\n";
		std::cout << "Departure: Amsterdam, The Netherlands\n";
		std::cout << "Desitnation: Oslo, Norway\n";
		std::cout << "Time since takeoff: " << time_since_takeoff << " min \n";
		std::cout << std::flush;
		
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	}
}

Once the output has reached the bottom of the output window it will look sort of like the text is being updated because the new lines will appear where the old lines were.

Again, if you insert a flush and a small delay between outputting two lines (to simulate that you're calling some costly function while outputting) you'll see that it doesn't look as nice.
Last edited on
There is a utility, unbuffer, which causes the program being executed to run with unbuffered output.
https://man.archlinux.org/man/unbuffer.1.en
Note that whilst you might want explicit buffer flush for cout (eg std::endl etc), you don't for file streams. You want these to perform as fast as possible - hence don't use std::endl etc when writing to a file stream. If the code uses std::cin/cout but is expected to be used with file re-direction, then the same applies - don't do explicit stream flush.
Topic archived. No new replies allowed.