xref: /freebsd/contrib/llvm-project/libcxx/src/iostream.cpp (revision 95b4436e989df29f6368f13832cb13d7cbc52eac)
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