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