Option 1: set stdin & stdout
According to cppreference.com:
By default, all eight standard C++ streams are synchronized with their respective C streams.
And as long as you didn't explicitly called sync_with_stdio(false)
, they'll stay that way. What does it mean? The following:
In practice, this means that the synchronized C++ streams are unbuffered, and each I/O operation on a C++ stream is immediately applied to the corresponding C stream's buffer. This makes it possible to freely mix C++ and C I/O.
So, flush()
-ing your cin
& cout
before dup()
-ing them should be enough, since they should be in a consistent state.
If you wish to work with files for example, you could use:
if (freopen("input.txt", "r", stdin) == NULL) {
// Handle error, errno is set to indicate error
}
if (freopen("output.txt", "w", stdout) == NULL) {
// Handle error, errno is set to indicate error
}
Note 1: Setting the global extern FILE * stdin
or stdout
won't work because it simply changes a single instance of a pointer to the relevant FILE
struct of the os. Any module that copied this pointer at any moment prior to this change will continue using the old FILE
. A specific example is libc++'s implementation for cout
, which copies FILE * stdout
to a private member during the object's init. freopen
on the other hand changes the internal FILE
structure of the OS to use the newly opened file, affecting anyone who has a FILE *
to it.
Note 2: When using dup()
flavors (rather than freopen()
), we are changing the underlying fd
, rather than the FILE*
. The freopen()
method does more than that. From POSIX:
The freopen() function shall first attempt to flush the stream associated with stream as if by a call to fflush(stream). Failure to flush the stream successfully shall be ignored. If pathname is not a null pointer, freopen() shall close any file descriptor associated with stream. Failure to close the file descriptor successfully shall be ignored. The error and end-of-file indicators for the stream shall be cleared.
dup()
-ing might work, but, it might be tricky, since it won't affect other properties of the FILE*
, including: Character width, Buffering state, The buffer, I/O, Binary/text mode indicator, End-of-file status indicator, Error status indicator, File position indicator & (After C++17) Reentrant lock used to prevent data races.
When possible, I suggest using freopen
. Otherwise, you could follow the steps described by yourself (fflush()
, clearerr()
). Skipping fclose()
will be wise, since we won't be able to reopen the same internal FILE
by any of the API methods.
Option 2: set cin's & cout's rdbuf()
The other way around, just like some comments proposed, is replacing cin
's and cout
's underlying buffer using rdbuf()
.
What are your options here?
File streams: Open ifstream
& ofstream
and use them:
std::ifstream fin("input.txt");
if (!fin) {
// Handle error
}
cin.rdbuf(fin.rdbuf());
std::ofstream fout("output.txt");
if (!fout) {
// Handle error
}
cout.rdbuf(fout.rdbuf());
Network streams: Use boost's boost::asio::ip::tcp::iostream
(It's derived from std::streambuf
and thus will work):
boost::asio::ip::tcp::iostream stream("www.boost.org", "http");
if (!stream) {
// Handle error
}
cin.rdbuf(stream.rdbuf());
cout.rdbuf(stream.rdbuf());
// GET request example
cout << "GET /LICENSE_1_0.txt HTTP/1.0\r\n";
cout << "Host: www.boost.org\r\n";
cout << "Accept: */*\r\n";
cout << "Connection: close\r\n\r\n";
cout.flush();
std::string response;
std::getline(cin, response);
Custom streams: Use your own custom wrapper for std::streambuf
. See an example here.