46

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 and std::wcout control output to a stream buffer of implementation-defined type (derived from std::streambuf), associated with the standard C output stream stdout.

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 a std::basic_streambuf object.std::cout is initialized using a pointer to an instance of some implementation-defined derived class of std::basic_streambuf.

, which moves me closer to the answer.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
John
  • 2,963
  • 11
  • 33
  • It can create itself. – user207421 Jun 21 '22 at 04:58
  • 3
    You can create an `std::ostream` instance, but you can't default-construct it. [See here](https://en.cppreference.com/w/cpp/io/basic_ostream/basic_ostream) for more on the constructors. The page on [std::cout](https://en.cppreference.com/w/cpp/io/cout) has more details about how it's initialized--it's an implementation-defined stream buffer that it's connected to, and the implementation also has to guarantee that it's initialized appropriately early in the program. – Nathan Pierson Jun 21 '22 at 04:59
  • `cout` can be created as an instance of a class that derives from `std::ostream` – Drew McGowen Jun 21 '22 at 05:00
  • 1
    @DrewMcGowen But `cout` actually is an instance of `std::ostream` other than derived from `std::ostream`. – John Jun 21 '22 at 05:02
  • 1
    @DrewMcGowen Is that actually true? The [standard](https://eel.is/c++draft/iostream.syn) requires the declaration to be `extern ostream cout;`, which would seem to preclude `cout` being a reference to some derived class. – Nathan Pierson Jun 21 '22 at 05:03
  • Whoops, I lied - I was thinking about pointers, but here pointers aren't involved – Drew McGowen Jun 21 '22 at 05:04
  • @NathanPierson Thank you for the reply. But the link seems not very meanningful. 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 infomation is *The global objects std::cout and std::wcout control output to a stream buffer of implementation-defined type (derived from std::streambuf), associated with the standard C output stream stdout.* And nothing more. – John Jun 21 '22 at 05:08
  • 1
    The language standard defines what you see as a programmer, and what you get. It isn't supposed to tell you how it's implemented. – user207421 Jun 21 '22 at 05:10
  • 1
    @John The spec is deliberately vague here to give library implementers some leeway in how they choose to code things up. It’s written more like a contract that says what needs to be done rather than saying how specifically it should be done. – templatetypedef Jun 21 '22 at 05:11
  • 2
    Yes, that's correct. `std::basic_ostream` has a constructor that takes a pointer to a `std::basic_streambuf` object. `std::cout` is initialized using a pointer to an instance of some implementation-defined derived class of `std::basic_streambuf`. If you want more details, you need to specify what implementation you're asking about. – Nathan Pierson Jun 21 '22 at 05:12
  • @NathanPierson I see. Thank you for the detailed explanation. I am working on `Ubuntu`. After I have read your last reply, I think I almost understand the statement in your link. *The global objects std::cout and std::wcout control output to a **stream buffer** of implementation-defined type (derived from std::streambuf),* – John Jun 21 '22 at 05:13
  • The name of the operating system does not tell us the name of the C++ compiler. Please read the edit history to see why your question was edited. – user207421 Jun 21 '22 at 05:16
  • 8
    It sounds like you won't be satisfied until you see the actual code for a standard library implementation. So, you may want to browse the code for the GNU C++ standard library as a starting point. – paddy Jun 21 '22 at 05:18
  • @user207421 I see. Sorry, I updated the post. It's gcc 4.9. – John Jun 21 '22 at 05:19
  • gcc 4.9 is rather old. Don't use it. – n. m. could be an AI Jun 21 '22 at 06:05
  • 1
    `ostream* cout_ptr = ::new(cout) ostream(::new(__cout) __stdoutbuf(stdout, &mb_cout));` just the placement new in the initializing code. – 273K Jun 21 '22 at 06:15
  • 3
    @John it's up to the implementation how to implement the behaviour specified by the standard. There is no one answer, there is just what each implementation decided to do . – M.M Jun 21 '22 at 06:22
  • @n.1.8e9-where's-my-sharem. It's really out of my control. I have no choice, indeed. – John Jun 21 '22 at 06:27

3 Answers3

42

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 .

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Huh. So within `globals_io.cc`, `std::cin`/`std::cout`/`std::cerr` are nothing but `char` arrays aligned to match the requirements of `istream`/`ostream`. Yet the `iostream` header and `ios_init.cc` declares them as externs that are actual `istream`/`ostream` objects, and uses them as such (assuming [correctly] they haven't been initialized in the latter case, since it placement `new`s all over that memory), and I guess the linker is okay with that because it just cares that the correct amount of aligned memory is where it expects it to be? Weird. – ShadowRanger Jun 21 '22 at 17:18
  • 3
    @ShadowRanger The linkers, as a rule, do not even care about the size and alignment, although theoretically they could... but many compilers don't bother with properly filling the `size` fields in the object files' symbol tables, so the linkers have to cope with that. – Joker_vD Jun 21 '22 at 20:06
  • 2
    @ShadowRanger LTO does complain about it, but only if you build a static version of libstdc++ with LTO, which is far from the default. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59472 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64275 – Marc Glisse Jun 22 '22 at 17:12
  • MSVC uses similar trickery to construct its `cout`, `cin`, and `cerr`, as well; I'm not up-to-date on the specifics, though, last time I checked was either the 2010 or 2015 build. – Justin Time - Reinstate Monica Jun 22 '22 at 19:56
17

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).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
-5

I think a part of what the current answers are missing, and what is part of your question:
the name std::cout is also ‘magical’.
This means that the standard library knows about it, and provides the OS-specific necessary connections to the terminal; using the respective (and OS-specific) system calls for output, etc.

Aganju
  • 6,295
  • 1
  • 12
  • 23
  • Thank you for the explanation.Any reference?What's background piping?Could you please explain that in more details for me? – John Jun 22 '22 at 16:06