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 .