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 | template <class StreamT, class BufferT> |
20 | union stream_data { |
21 | constexpr stream_data() {} |
22 | constexpr ~stream_data() {} |
23 | struct { |
24 | // The stream has to be the first element, since that's referenced by the stream declarations in <iostream> |
25 | StreamT stream; |
26 | BufferT buffer; |
27 | mbstate_t mb; |
28 | }; |
29 | |
30 | void init(FILE* stdstream) { |
31 | mb = {}; |
32 | std::construct_at(&buffer, stdstream, &mb); |
33 | std::construct_at(&stream, &buffer); |
34 | } |
35 | }; |
36 | |
37 | #define CHAR_MANGLING_char "D" |
38 | #define CHAR_MANGLING_wchar_t "_W" |
39 | #define CHAR_MANGLING(CharT) CHAR_MANGLING_##CharT |
40 | |
41 | #ifdef _LIBCPP_COMPILER_CLANG_BASED |
42 | # define STRING_DATA_CONSTINIT constinit |
43 | #else |
44 | # define STRING_DATA_CONSTINIT |
45 | #endif |
46 | |
47 | #ifdef _LIBCPP_ABI_MICROSOFT |
48 | # define STREAM(StreamT, BufferT, CharT, var) \ |
49 | STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var __asm__( \ |
50 | "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT \ |
51 | "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12@A") |
52 | #else |
53 | # define STREAM(StreamT, BufferT, CharT, var) STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var |
54 | #endif |
55 | |
56 | // These definitions and the declarations in <iostream> technically cause ODR violations, since they have different |
57 | // types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU. |
58 | |
59 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, char, cin); |
60 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cout); |
61 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cerr); |
62 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, clog); |
63 | #if _LIBCPP_HAS_WIDE_CHARACTERS |
64 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, wchar_t, wcin); |
65 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout); |
66 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr); |
67 | _LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog); |
68 | #endif // _LIBCPP_HAS_WIDE_CHARACTERS |
69 | |
70 | // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority |
71 | // attribute with a value that's reserved for the implementation (we're the implementation). |
72 | #include "iostream_init.h" |
73 | |
74 | // On Windows the TLS storage for locales needs to be initialized before we create |
75 | // the standard streams, otherwise it may not be alive during program termination |
76 | // when we flush the streams. |
77 | static void force_locale_initialization() { |
78 | #if defined(_LIBCPP_MSVCRT_LIKE) |
79 | static bool once = []() { |
80 | auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C" , 0); |
81 | { |
82 | __locale::__locale_guard g(loc); // forces initialization of locale TLS |
83 | ((void)g); |
84 | } |
85 | __locale::__freelocale(loc); |
86 | return true; |
87 | }(); |
88 | ((void)once); |
89 | #endif |
90 | } |
91 | |
92 | class DoIOSInit { |
93 | public: |
94 | DoIOSInit(); |
95 | ~DoIOSInit(); |
96 | }; |
97 | |
98 | DoIOSInit::DoIOSInit() { |
99 | force_locale_initialization(); |
100 | |
101 | cin.init(stdin); |
102 | cout.init(stdout); |
103 | cerr.init(stderr); |
104 | clog.init(stderr); |
105 | |
106 | cin.stream.tie(&cout.stream); |
107 | std::unitbuf(base&: cerr.stream); |
108 | cerr.stream.tie(&cout.stream); |
109 | |
110 | #if _LIBCPP_HAS_WIDE_CHARACTERS |
111 | wcin.init(stdin); |
112 | wcout.init(stdout); |
113 | wcerr.init(stderr); |
114 | wclog.init(stderr); |
115 | |
116 | wcin.stream.tie(&wcout.stream); |
117 | std::unitbuf(wcerr.stream); |
118 | wcerr.stream.tie(&wcout.stream); |
119 | #endif |
120 | } |
121 | |
122 | DoIOSInit::~DoIOSInit() { |
123 | cout.stream.flush(); |
124 | clog.stream.flush(); |
125 | |
126 | #if _LIBCPP_HAS_WIDE_CHARACTERS |
127 | wcout.stream.flush(); |
128 | wclog.stream.flush(); |
129 | #endif |
130 | } |
131 | |
132 | ios_base::Init::Init() { |
133 | static DoIOSInit init_the_streams; // gets initialized once |
134 | } |
135 | |
136 | ios_base::Init::~Init() {} |
137 | |
138 | _LIBCPP_END_NAMESPACE_STD |
139 | |