MAIN

Clearing flush() Dilemma Usage in iostream

We often hear a few questions revolving around flushing the stream especially from std::cout, std::cerr, and similar others.

Those questions might be

Let’s get above tone of questions answered by studying source code of three major compilers out there namely GCC, Clang, and MSVC. Specific version won’t be matter much as the topic at hands regarding to iostream is rarely changing from version to version, or release to release. It’s there.

All compilers’ source code we will be inspecting are provided through git repository

How do those streams constructed?

Mostly I just use grep to search through things. With this particular example to see how it’s constructed, you can try grep -nr tie.*cout then it would likely take you to the source file which includes construction of stream objects. Why search for tie? Because it’s the one time setup to set one stream to depend(-ish) on another stream.

Seeing source code related to this section alone can answer several questions we had in mind already. Let’s list out such code first below.

GCC

Take a look at libstdc++-v3/src/c++98/ios_init.cc.

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

#ifdef _GLIBCXX_USE_WCHAR_T
    new (&buf_wcout_sync) stdio_sync_filebuf<wchar_t>(stdout);
    new (&buf_wcin_sync) stdio_sync_filebuf<wchar_t>(stdin);
    new (&buf_wcerr_sync) stdio_sync_filebuf<wchar_t>(stderr);

    new (&wcout) wostream(&buf_wcout_sync);
    new (&wcin) wistream(&buf_wcin_sync);
    new (&wcerr) wostream(&buf_wcerr_sync);
    new (&wclog) wostream(&buf_wcerr_sync);
    wcin.tie(&wcout);
    wcerr.setf(ios_base::unitbuf);
    wcerr.tie(&wcout);
#endif

    // NB: Have to set refcount above one, so that standard
    // streams are not re-initialized with uses of ios_base::Init
    // besides <iostream> static object, ie just using <ios> with
    // ios_base::Init objects.
    __gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1);
      }
  }

Clang

Look at libcxx/src/iostream.cpp.

DoIOSInit::DoIOSInit()
{
    force_locale_initialization();

#ifndef _LIBCPP_HAS_NO_STDIN
    istream* cin_ptr  = ::new(cin)  istream(::new(__cin)  __stdinbuf <char>(stdin, &mb_cin));
    wistream* wcin_ptr  = ::new(wcin)  wistream(::new(__wcin)  __stdinbuf <wchar_t>(stdin, &mb_wcin));
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
    ostream* cout_ptr = ::new(cout) ostream(::new(__cout) __stdoutbuf<char>(stdout, &mb_cout));
    wostream* wcout_ptr = ::new(wcout) wostream(::new(__wcout) __stdoutbuf<wchar_t>(stdout, &mb_wcout));
#endif
    ostream* cerr_ptr = ::new(cerr) ostream(::new(__cerr) __stdoutbuf<char>(stderr, &mb_cerr));
                        ::new(clog) ostream(cerr_ptr->rdbuf());
    wostream* wcerr_ptr = ::new(wcerr) wostream(::new(__wcerr) __stdoutbuf<wchar_t>(stderr, &mb_wcerr));
                          ::new(wclog) wostream(wcerr_ptr->rdbuf());

#if !defined(_LIBCPP_HAS_NO_STDIN) && !defined(_LIBCPP_HAS_NO_STDOUT)
    cin_ptr->tie(cout_ptr);
    wcin_ptr->tie(wcout_ptr);
#endif
    _VSTD::unitbuf(*cerr_ptr);
    _VSTD::unitbuf(*wcerr_ptr);
#ifndef _LIBCPP_HAS_NO_STDOUT
    cerr_ptr->tie(cout_ptr);
    wcerr_ptr->tie(wcout_ptr);
#endif
}

MSVC

Look at stl/src/cout.cpp, stl/src/cerr.cpp, stl/src/cin.cpp, and stl/src/clog.cpp.

(stl/src/cout.cpp)

__PURE_APPDOMAIN_GLOBAL static filebuf fout(_cpp_stdout);

#if defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern ostream cout(&fout);

#else // defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream cout(&fout);
#endif // defined(_M_CEE_PURE)

struct _Init_cout { // ensures that cout is initialized
    __CLR_OR_THIS_CALL _Init_cout() { // initialize cout
        _Ptr_cout = &cout;
        if (_Ptr_cin != nullptr) {
            _Ptr_cin->tie(_Ptr_cout);
        }

        if (_Ptr_cerr != nullptr) {
            _Ptr_cerr->tie(_Ptr_cout);
        }

        if (_Ptr_clog != nullptr) {
            _Ptr_clog->tie(_Ptr_cout);
        }
    }
};

(stl/src/cerr.cpp)

__PURE_APPDOMAIN_GLOBAL static filebuf ferr(_cpp_stderr);

#if defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern ostream cerr(&ferr);

#else // defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream cerr(&ferr);
#endif // defined(_M_CEE_PURE)

struct _Init_cerr { // ensures that cerr is initialized
    __CLR_OR_THIS_CALL _Init_cerr() { // initialize cerr
        _Ptr_cerr = &cerr;
        cerr.tie(_Ptr_cout);
        cerr.setf(ios_base::unitbuf);
    }
};

(stl/src/cin.cpp)

__PURE_APPDOMAIN_GLOBAL static filebuf fin(_cpp_stdin);

#if defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern istream cin(&fin);

#else // defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT istream cin(&fin);
#endif // defined(_M_CEE_PURE)

struct _Init_cin { // ensures that cin is initialized
    __CLR_OR_THIS_CALL _Init_cin() { // initialize cin
        _Ptr_cin = &cin;
        cin.tie(_Ptr_cout);
    }
};

(stl/src/clog.cpp)

__PURE_APPDOMAIN_GLOBAL static filebuf flog(_cpp_stderr);

#if defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern ostream clog(&flog);

#else // defined(_M_CEE_PURE)
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream clog(&flog);
#endif // defined(_M_CEE_PURE)

struct _Init_clog { // ensures that clog is initialized
    __CLR_OR_THIS_CALL _Init_clog() { // initialize clog
        _Ptr_clog = &clog;
        clog.tie(_Ptr_cout);
    }
};

From all above, We can conclude that

How do those streams destructed?

GCC

(libstdc++-v3/src/c++98/ios_init.cc)

  ios_base::Init::~Init()
  {
    // Be race-detector-friendly.  For more info see bits/c++config.
    _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_S_refcount);
    if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, -1) == 2)
      {
        _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_S_refcount);
    // Catch any exceptions thrown by basic_ostream::flush()
    __try
      {
        // Flush standard output streams as required by 27.4.2.1.6
        cout.flush();
        cerr.flush();
        clog.flush();

#ifdef _GLIBCXX_USE_WCHAR_T
        wcout.flush();
        wcerr.flush();
        wclog.flush();
#endif
      }
    __catch(...)
      { }
      }
  }

Clang

(libcxx/src/iostream.cpp)

DoIOSInit::~DoIOSInit()
{
#ifndef _LIBCPP_HAS_NO_STDOUT
    ostream* cout_ptr = reinterpret_cast<ostream*>(cout);
    wostream* wcout_ptr = reinterpret_cast<wostream*>(wcout);
    cout_ptr->flush();
    wcout_ptr->flush();
#endif

    ostream* clog_ptr = reinterpret_cast<ostream*>(clog);
    wostream* wclog_ptr = reinterpret_cast<wostream*>(wclog);
    clog_ptr->flush();
    wclog_ptr->flush();
}

MSVC

Its implementation is slightly different than the other twos. stl/inc/xiosbase orchestrates the call to constructor and destructor which are _Init_ctor() and _Init_dtor() respectively.

The actual implementation of destructor function can be seen at stl/src/iostream.cpp, stl/src/wiostrea.cpp.

(stl/src/iostream.cpp)

_CRTIMP2_PURE void __cdecl ios_base::Init::_Init_dtor(ios_base::Init*) { // flush standard streams last time
    if (--_Init_cnt == 0) { // flush standard streams
        if (_Ptr_cerr != nullptr) {
            _Ptr_cerr->flush();
        }

        if (_Ptr_clog != nullptr) {
            _Ptr_clog->flush();
        }

        if (_Ptr_cout != nullptr) {
            _Ptr_cout->flush();
        }
    }
}

From above, we can conclude that

Take a look at std::endl

Alternatively, instead of inspecting from withing git repository, we can also just take a look at header file on your system e.g. /usr/include/c++/9/ostream for GCC v.9.x as it is an inline implementation. But I will reference from git repository for consistency as did previously.

GCC

(libstdc++-v3/include/std/ostream)

  template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    endl(basic_ostream<_CharT, _Traits>& __os)
    { return flush(__os.put(__os.widen('\n'))); }

Clang

(libcxx/include/ostream)

template <class _CharT, class _Traits>
inline
basic_ostream<_CharT, _Traits>&
endl(basic_ostream<_CharT, _Traits>& __os)
{
    __os.put(__os.widen('\n'));
    __os.flush();
    return __os;
}

MSVC

(stl/inc/ostream)

template <class _Elem, class _Traits>
basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL endl(
    basic_ostream<_Elem, _Traits>& _Ostr) { // insert newline and flush stream
    _Ostr.put(_Ostr.widen('\n'));
    _Ostr.flush();
    return _Ostr;
}

From above, we can conclude that



First published on June, 14, 2021

June, 14, 2021






Written by Wasin Thonkaew
In case of reprinting, comments, suggestions
or to do anything with the article in which you are unsure of, please
write e-mail to wasin[add]wasin[dot]io

Copyright © 2019-2021 Wasin Thonkaew. All Rights Reserved.