std::cout
is an instance of std::ostream
. I can see the declaration of std::cout
in a file named /usr/include/c /7/iostream
:
extern ostream cout; /// Linked to standard output
And std::ostream
is defined by typedef std::basic_ostream<char> std::ostream
.
What's more, it seems that you can't create an instance of std::ostream
. See this demo code snippet:
#include<iostream>
int main()
{
std::ostream os;
return 0;
}
Here is what the compiler complains about the code snippet above:
In file included from /opt/compiler-explorer/gcc-4.9.0/include/c /4.9.0/iostream:39:0,
from <source>:1:
/opt/compiler-explorer/gcc-4.9.0/include/c /4.9.0/ostream: In function 'int main()':
/opt/compiler-explorer/gcc-4.9.0/include/c /4.9.0/ostream:384:7: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char; _Traits = std::char_traits<char>]' is protected
basic_ostream()
^
<source>:5:18: error: within this context
std::ostream os;
^
The question arises, since the std::basic_ostream<_CharT, _Traits>::basic_ostream()
is marked protected, how std::cout
is created?
This link on CppReference seems not very meaningful. It does not clearly tell me how std::cout
is implemented and how std::cout
is created by the constructor of std::ostream
. As far as I can see, the most related information is:
The global objects
std::cout
andstd::wcout
control output to a stream buffer of implementation-defined type (derived fromstd::streambuf
), associated with the standard C output streamstdout
.
And nothing more.
I am working on Ubuntu
with gcc 4.9
Thanks to @NathanPierson.
He told me that
std::basic_ostream
has a constructor that takes a pointer to astd::basic_streambuf
object.std::cout
is initialized using a pointer to an instance of some implementation-defined derived class ofstd::basic_streambuf
.
, which moves me closer to the answer.
CodePudding user response:
A compiler and its standard library implementation may cooperate using non-standard features which are not usable by mere programmers.
This is not necessary in this case because there is a quite standard public constructor:
explicit basic_ostream(basic_streambuf<char_type, Traits>* sb);
If you have a streambuf
ready, you can create an object of type ostream
, and so can the standard library.
What is that streambuf
exactly is a hidden implementation detail, but on a typical implementation it is probably an object of a custom class constructed from stdout
(the C-style <cstdio>
file pointer).
CodePudding user response:
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 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++-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 contsructor of ios_base::Init
class, in https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/src/c++98/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 istream
/ostream
uses to store input/output data, with implementation here https://github.com/gcc-mirror/gcc/blob/master/libstdc++-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++-v3/src/c++98/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 .