1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "std_stream.h" 10 11 #include <__memory/construct_at.h> 12 #include <__ostream/basic_ostream.h> 13 #include <istream> 14 15 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE) 16 17 _LIBCPP_BEGIN_NAMESPACE_STD 18 19 // This file implements the various stream objects provided inside <iostream>. We're doing some ODR violations in here, 20 // so this quite fragile. Specifically, the size of the stream objects (i.e. cout, cin etc.) needs to stay the same. 21 // For that reason, we have `stream` and `stream_data` separated into two objects. The public `stream` objects only 22 // contain the actual stream, while the private `stream_data` objects contains the `basic_streambuf` we're using as well 23 // as the mbstate_t. `stream_data` objects are only accessible within the library, so they aren't ABI sensitive and we 24 // can change them as we want. 25 26 template <class StreamT> 27 union stream { 28 constexpr stream() {} 29 stream(const stream&) = delete; 30 stream& operator=(const stream&) = delete; 31 constexpr ~stream() {} 32 33 StreamT value; 34 }; 35 36 template <class StreamT, class BufferT> 37 union stream_data { 38 constexpr stream_data() {} 39 constexpr ~stream_data() {} 40 struct { 41 BufferT buffer; 42 mbstate_t mb; 43 }; 44 }; 45 46 template <class StreamT, class BufferT> 47 void init_stream(FILE* stdstream, stream<StreamT>& stream, stream_data<StreamT, BufferT>& data) { 48 data.mb = {}; 49 std::construct_at(&data.buffer, stdstream, &data.mb); 50 std::construct_at(&stream.value, &data.buffer); 51 } 52 53 #define CHAR_MANGLING_char "D" 54 #define CHAR_MANGLING_wchar_t "_W" 55 #define CHAR_MANGLING(CharT) CHAR_MANGLING_##CharT 56 57 #ifdef _LIBCPP_COMPILER_CLANG_BASED 58 # define STRING_DATA_CONSTINIT constinit 59 #else 60 # define STRING_DATA_CONSTINIT 61 #endif 62 63 #ifdef _LIBCPP_ABI_MICROSOFT 64 # define STREAM(StreamT, BufferT, CharT, var) \ 65 STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data; \ 66 _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var __asm__( \ 67 "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT \ 68 "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12@A") 69 #else 70 # define STREAM(StreamT, BufferT, CharT, var) \ 71 STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var##_data; \ 72 _LIBCPP_EXPORTED_FROM_ABI STRING_DATA_CONSTINIT stream<StreamT<CharT>> var 73 #endif 74 75 // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different 76 // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU. 77 78 STREAM(basic_istream, __stdinbuf, char, cin); 79 STREAM(basic_ostream, __stdoutbuf, char, cout); 80 STREAM(basic_ostream, __stdoutbuf, char, cerr); 81 STREAM(basic_ostream, __stdoutbuf, char, clog); 82 #if _LIBCPP_HAS_WIDE_CHARACTERS 83 STREAM(basic_istream, __stdinbuf, wchar_t, wcin); 84 STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout); 85 STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr); 86 STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog); 87 #endif // _LIBCPP_HAS_WIDE_CHARACTERS 88 89 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority 90 // attribute with a value that's reserved for the implementation (we're the implementation). 91 #include "iostream_init.h" 92 93 // On Windows the TLS storage for locales needs to be initialized before we create 94 // the standard streams, otherwise it may not be alive during program termination 95 // when we flush the streams. 96 static void force_locale_initialization() { 97 #if defined(_LIBCPP_MSVCRT_LIKE) 98 static bool once = []() { 99 auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0); 100 { 101 __locale::__locale_guard g(loc); // forces initialization of locale TLS 102 ((void)g); 103 } 104 __locale::__freelocale(loc); 105 return true; 106 }(); 107 ((void)once); 108 #endif 109 } 110 111 class DoIOSInit { 112 public: 113 DoIOSInit(); 114 ~DoIOSInit(); 115 }; 116 117 DoIOSInit::DoIOSInit() { 118 force_locale_initialization(); 119 120 init_stream(stdin, cin, cin_data); 121 init_stream(stdout, cout, cout_data); 122 init_stream(stderr, cerr, cerr_data); 123 init_stream(stderr, clog, clog_data); 124 125 cin.value.tie(&cout.value); 126 std::unitbuf(cerr.value); 127 cerr.value.tie(&cout.value); 128 129 #if _LIBCPP_HAS_WIDE_CHARACTERS 130 init_stream(stdin, wcin, wcin_data); 131 init_stream(stdout, wcout, wcout_data); 132 init_stream(stderr, wcerr, wcerr_data); 133 init_stream(stderr, wclog, wclog_data); 134 135 wcin.value.tie(&wcout.value); 136 std::unitbuf(wcerr.value); 137 wcerr.value.tie(&wcout.value); 138 #endif 139 } 140 141 DoIOSInit::~DoIOSInit() { 142 cout.value.flush(); 143 clog.value.flush(); 144 145 #if _LIBCPP_HAS_WIDE_CHARACTERS 146 wcout.value.flush(); 147 wclog.value.flush(); 148 #endif 149 } 150 151 ios_base::Init::Init() { 152 static DoIOSInit init_the_streams; // gets initialized once 153 } 154 155 ios_base::Init::~Init() {} 156 157 _LIBCPP_END_NAMESPACE_STD 158