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