Why Do We Need to Tie Std::Cin and Std::Cout

Why do we need to tie std::cin and std::cout?

There is nothing wrong in your example (except that you should add a semi-colon after the cin.tie(0) line), nor with the way iostream objects work.

tie() simply guarantees the flushing of cout before cin executes an input. This is useful for the user to see the question before being asked for the answer.

However, if you un-tie() the cin from cout, there is no guarantee that the buffer of the cout is flushed. But there is no guarantee that the buffer is un-flushed neither. In fact, if the computer has enough resources, it will flush the cout buffer immediately, so this occurs before cin asking for the input. This is the case in your example.

So, everything works well. Except that after cin.tie(0), there is no guarantee that the flush-ing will occur. However, in 99% of the cases, that flush-ing will still occur (but it is no longer guaranteed).

In theory, if tied, cin and cout could share the same buffer. But, I think no implementation does that. One reason is that the two may be un-tie()d.

Using std::endl vs \n when cin and cout are untied

how do I ensure that the buffer doesn't get overflowed,

The output buffer doesn't "overflow". When it gets full, it is automatically flushed, i.e. its contents are written out and its length is reset to 0. This is the case whether cin / cout are tied or not.

cin and cout work properly without blocking

You normally want operations on cin / cout to block. But again, blocking vs. non-blocking I/O has nothing to do with whether cin / cout are tied.

and buffer gets flushed properly when I am not using std::endl. Does the use of "\n" automatically handles it?

Outputting '\n' only flushes the buffer if the stream is in line-buffered mode. cout is automatically put in line-buffered mode if output goes to a terminal; otherwise it is block buffered (i.e. it only gets flushed when it runs full).

In a programming competition cout usually goes to a pipe or log file, so it will be block buffered and '\n' doesn't cause a flush. However, in that situation it also doesn't matter whether prompts are displayed before input is read (which is the normal use case for tied cin / cout). Just make sure you produce the right output and let the I/O library worry about buffering. The buffer is automatically flushed when it runs full, when the stream is closed, and when your program exits. No output is lost (unless your program crashes, but then you have other things to worry about).

How to prevent `std::cin` or `\n` from flushing the buffer of `std::cout`?

You could write to your own std::stringstream buffer, and then output that to std::cout when you're ready.

MWE:

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>

using std::cin;
using std::cout;
using std::istream;
using std::runtime_error;
using std::stringstream;
using std::vector;

static auto get_int(istream& in) -> int {
int n;
if (!(in >> n)) {
throw runtime_error("bad input");
}
return n;
}

int main() {
auto ss = stringstream();
auto v = vector<int>();
auto size = get_int(cin);

while(size--) {
auto n1 = get_int(cin);

if (n1 == 0) {
auto n2 = get_int(cin);
v.push_back(n2);
} else if (n1 == 1) {
auto n2 = get_int(cin);
ss << v[n2] << '\n';
} else if (n1 == 2) {
v.pop_back();
}
}

cout << ss.str();
}

When i use cout.tie(NULL), program doesn't print anything for my code, but if i print endl, program works fine

std::cout will flush under these conditions:

  1. An input-stream which is tied to std::cout tries to read input.

    You removed the ties.

  2. iostreams are synchronized with stdio, thus effectively unbuffered.

    You disabled the synchronization.

  3. The buffer is full.

    That takes a bit longer.

  4. The program ends normally.

    That comes too late for you.

  5. There is a manual flush (stream.flush() which is called when streaming std::flush; stream << std::endl is equivalent to stream << stream.widen('\n') << std::flush).

    You have none of those.

So, fix any of them and you will see your output earlier.

Is explicit flush necessary for interleaved cout and cin operations?

The stream std::cout is tied to std::cin by default: the stream pointed to by stream.tie() is flushed prior to every properly implement input operation. Unless you changed the stream tied to std::cin, there is no need to flush std::cout before using std::cin as it will be done implicitly.

The actual logic to flush the stream happens when constructing a std::istream::sentry with an input stream: when the input stream isn't in failure state, the stream pointed to by stream.tie() is flushed. Of course, this assumes that the input operators look something like this:

std::istream& operator>> (std::istream& in, T& value) {
std::istream::sentry cerberos(in);
if (sentry) {
// read the value
}
return in;
}

The standard stream operations are implemented this way. When user's input operations are not implemented in this style and do their input directly using the stream buffer, the flush won't happen. The error is, of course, in the input operators.

How is `std::cout` implemented?

how std::cout is created?

First things first, from https://en.cppreference.com/w/cpp/io/ios_base/Init :

std::ios_base::Init

This class is used to ensure that the default C++ streams (std::cin,
std::cout, etc.) are properly initialized and destructed. [...]

The header <iostream> behaves as if it defines (directly or
indirectly) an instance of std::ios_base::Init with static storage
duration: [...]

Meh, let's do a real code example. I will be using GCC C++ library. From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/iostream#L73 , this is the important part:

 // For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;

Now we jump to the constructor of ios_base::Init class, in https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/ios_init.cc#L85 :

ios_base::Init::Init()
{
if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0)
{
// Standard streams default to synced with "C" operations.
_S_synced_with_stdio = true;

new (&buf_cout_sync) stdio_sync_filebuf<char>(stdout);
new (&buf_cin_sync) stdio_sync_filebuf<char>(stdin);
new (&buf_cerr_sync) stdio_sync_filebuf<char>(stderr);

// The standard streams are constructed once only and never
// destroyed.
new (&cout) ostream(&buf_cout_sync);
new (&cin) istream(&buf_cin_sync);
new (&cerr) ostream(&buf_cerr_sync);
new (&clog) ostream(&buf_cerr_sync);
cin.tie(&cout);
cerr.setf(ios_base::unitbuf);
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 455. cerr::tie() and wcerr::tie() are overspecified.
cerr.tie(&cout);

The _S_refcount is there for when you would call ios_base::Init::Init(); manually from a constructor of a static class, it protects against double initialization.

The stdio_sync_filebuf is an internal buffer for istream/ostream and it is meant to handle cstdio FILE* operations to get/put input/output data, with implementation here https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/ext/stdio_sync_filebuf.h#L56 . It inherits from std::basic_streambuf.

So cout is constructed in-place with stdio_sync_filebuf<char> as parameter. It is the first constructor mentioned here https://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream .

Now, because the stuff is constructed in-place, you might wonder how is the memory allocated? From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/globals_io.cc#L50 :

  // Standard stream objects.
// NB: Iff <iostream> is included, these definitions become wonky.
typedef char fake_istream[sizeof(istream)]
__attribute__ ((aligned(__alignof__(istream))));
typedef char fake_ostream[sizeof(ostream)]
__attribute__ ((aligned(__alignof__(ostream))));
fake_istream cin;
fake_ostream cout;
fake_ostream cerr;
fake_ostream clog;

The objects are just empty char buffers of proper size and proper alignment.

And yes, you can construct ostream yourself, with __gnu_cxx::stdio_sync_filebuf on GCC:

#include <fstream>
#include <ext/stdio_sync_filebuf.h>
int main() {
__gnu_cxx::stdio_sync_filebuf<char> mybuf_cout_sync(stdout);
std::ostream os(&mybuf_cout_sync);
os << "Hello world!\n";
return 0;
}

Or, to be portable, you would write your own class that inherits from std::streambuf and construct ostream from it yourself. There are many examples online, like for example here https://stackoverflow.com/a/51250032/9072753 .

Significance of ios_base::sync_with_stdio(false); cin.tie(NULL);

The two calls have different meanings that have nothing to do with performance; the fact that it speeds up the execution time is (or might be) just a side effect. You should understand what each of them does and not blindly include them in every program because they look like an optimization.

ios_base::sync_with_stdio(false);

This disables the synchronization between the C and C++ standard streams. By default, all standard streams are synchronized, which in practice allows you to mix C- and C++-style I/O and get sensible and expected results. If you disable the synchronization, then C++ streams are allowed to have their own independent buffers, which makes mixing C- and C++-style I/O an adventure.

Also keep in mind that synchronized C++ streams are thread-safe (output from different threads may interleave, but you get no data races).

cin.tie(NULL);

This unties cin from cout. Tied streams ensure that one stream is flushed automatically before each I/O operation on the other stream.

By default cin is tied to cout to ensure a sensible user interaction. For example:

std::cout << "Enter name:";
std::cin >> name;

If cin and cout are tied, you can expect the output to be flushed (i.e., visible on the console) before the program prompts input from the user. If you untie the streams, the program might block waiting for the user to enter their name but the "Enter name" message is not yet visible (because cout is buffered by default, output is flushed/displayed on the console only on demand or when the buffer is full).

So if you untie cin from cout, you must make sure to flush cout manually every time you want to display something before expecting input on cin.

In conclusion, know what each of them does, understand the consequences, and then decide if you really want or need the possible side effect of speed improvement.



Related Topics



Leave a reply



Submit