Commit | Line | Data |
---|---|---|
05aa7e19 JG |
1 | // Formatting library for C++ - std::ostream support |
2 | // | |
3 | // Copyright (c) 2012 - present, Victor Zverovich | |
4 | // All rights reserved. | |
5 | // | |
6 | // For the license information refer to format.h. | |
7 | ||
8 | #ifndef FMT_OSTREAM_H_ | |
9 | #define FMT_OSTREAM_H_ | |
10 | ||
bd9231e4 JG |
11 | #ifndef FMT_MODULE |
12 | # include <fstream> // std::filebuf | |
8b75cd77 | 13 | #endif |
05aa7e19 | 14 | |
bd9231e4 JG |
15 | #ifdef _WIN32 |
16 | # ifdef __GLIBCXX__ | |
17 | # include <ext/stdio_filebuf.h> | |
18 | # include <ext/stdio_sync_filebuf.h> | |
19 | # endif | |
20 | # include <io.h> | |
21 | #endif | |
05aa7e19 | 22 | |
bd9231e4 | 23 | #include "chrono.h" // formatbuf |
05aa7e19 | 24 | |
bd9231e4 | 25 | FMT_BEGIN_NAMESPACE |
05aa7e19 JG |
26 | namespace detail { |
27 | ||
8b75cd77 JG |
28 | // Generate a unique explicit instantion in every translation unit using a tag |
29 | // type in an anonymous namespace. | |
30 | namespace { | |
31 | struct file_access_tag {}; | |
32 | } // namespace | |
bd9231e4 | 33 | template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr> |
8b75cd77 JG |
34 | class file_access { |
35 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } | |
36 | }; | |
37 | ||
38 | #if FMT_MSC_VERSION | |
39 | template class file_access<file_access_tag, std::filebuf, | |
40 | &std::filebuf::_Myfile>; | |
41 | auto get_file(std::filebuf&) -> FILE*; | |
8b75cd77 JG |
42 | #endif |
43 | ||
bd9231e4 JG |
44 | inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) |
45 | -> bool { | |
46 | FILE* f = nullptr; | |
47 | #if FMT_MSC_VERSION && FMT_USE_RTTI | |
8b75cd77 | 48 | if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) |
bd9231e4 JG |
49 | f = get_file(*buf); |
50 | else | |
51 | return false; | |
52 | #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI | |
8b75cd77 | 53 | auto* rdbuf = os.rdbuf(); |
bd9231e4 JG |
54 | if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) |
55 | f = sfbuf->file(); | |
8b75cd77 | 56 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) |
bd9231e4 | 57 | f = fbuf->file(); |
8b75cd77 JG |
58 | else |
59 | return false; | |
8b75cd77 | 60 | #else |
bd9231e4 JG |
61 | ignore_unused(os, data, f); |
62 | #endif | |
63 | #ifdef _WIN32 | |
64 | if (f) { | |
65 | int fd = _fileno(f); | |
66 | if (_isatty(fd)) { | |
67 | os.flush(); | |
68 | return write_console(fd, data); | |
69 | } | |
70 | } | |
8b75cd77 JG |
71 | #endif |
72 | return false; | |
73 | } | |
bd9231e4 JG |
74 | inline auto write_ostream_unicode(std::wostream&, |
75 | fmt::basic_string_view<wchar_t>) -> bool { | |
8b75cd77 JG |
76 | return false; |
77 | } | |
78 | ||
05aa7e19 JG |
79 | // Write the content of buf to os. |
80 | // It is a separate function rather than a part of vprint to simplify testing. | |
81 | template <typename Char> | |
82 | void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { | |
83 | const Char* buf_data = buf.data(); | |
84 | using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; | |
85 | unsigned_streamsize size = buf.size(); | |
86 | unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); | |
87 | do { | |
88 | unsigned_streamsize n = size <= max_size ? size : max_size; | |
89 | os.write(buf_data, static_cast<std::streamsize>(n)); | |
90 | buf_data += n; | |
91 | size -= n; | |
92 | } while (size != 0); | |
93 | } | |
94 | ||
95 | template <typename Char, typename T> | |
bd9231e4 | 96 | void format_value(buffer<Char>& buf, const T& value) { |
05aa7e19 JG |
97 | auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); |
98 | auto&& output = std::basic_ostream<Char>(&format_buf); | |
99 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) | |
bd9231e4 | 100 | output.imbue(std::locale::classic()); // The default is always unlocalized. |
05aa7e19 JG |
101 | #endif |
102 | output << value; | |
103 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | |
05aa7e19 JG |
104 | } |
105 | ||
bd9231e4 JG |
106 | template <typename T> struct streamed_view { |
107 | const T& value; | |
108 | }; | |
8b75cd77 JG |
109 | |
110 | } // namespace detail | |
111 | ||
05aa7e19 | 112 | // Formats an object of type T that has an overloaded ostream operator<<. |
8b75cd77 JG |
113 | template <typename Char> |
114 | struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { | |
115 | void set_debug_format() = delete; | |
05aa7e19 | 116 | |
bd9231e4 JG |
117 | template <typename T, typename Context> |
118 | auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { | |
05aa7e19 | 119 | auto buffer = basic_memory_buffer<Char>(); |
bd9231e4 | 120 | detail::format_value(buffer, value); |
05aa7e19 JG |
121 | return formatter<basic_string_view<Char>, Char>::format( |
122 | {buffer.data(), buffer.size()}, ctx); | |
123 | } | |
8b75cd77 JG |
124 | }; |
125 | ||
126 | using ostream_formatter = basic_ostream_formatter<char>; | |
05aa7e19 | 127 | |
8b75cd77 JG |
128 | template <typename T, typename Char> |
129 | struct formatter<detail::streamed_view<T>, Char> | |
130 | : basic_ostream_formatter<Char> { | |
bd9231e4 JG |
131 | template <typename Context> |
132 | auto format(detail::streamed_view<T> view, Context& ctx) const | |
133 | -> decltype(ctx.out()) { | |
8b75cd77 | 134 | return basic_ostream_formatter<Char>::format(view.value, ctx); |
05aa7e19 JG |
135 | } |
136 | }; | |
8b75cd77 JG |
137 | |
138 | /** | |
bd9231e4 JG |
139 | * Returns a view that formats `value` via an ostream `operator<<`. |
140 | * | |
141 | * **Example**: | |
142 | * | |
143 | * fmt::print("Current thread id: {}\n", | |
144 | * fmt::streamed(std::this_thread::get_id())); | |
8b75cd77 JG |
145 | */ |
146 | template <typename T> | |
bd9231e4 | 147 | constexpr auto streamed(const T& value) -> detail::streamed_view<T> { |
8b75cd77 JG |
148 | return {value}; |
149 | } | |
150 | ||
151 | namespace detail { | |
152 | ||
8b75cd77 JG |
153 | inline void vprint_directly(std::ostream& os, string_view format_str, |
154 | format_args args) { | |
155 | auto buffer = memory_buffer(); | |
156 | detail::vformat_to(buffer, format_str, args); | |
157 | detail::write_buffer(os, buffer); | |
158 | } | |
159 | ||
05aa7e19 JG |
160 | } // namespace detail |
161 | ||
bd9231e4 | 162 | FMT_EXPORT template <typename Char> |
8b75cd77 JG |
163 | void vprint(std::basic_ostream<Char>& os, |
164 | basic_string_view<type_identity_t<Char>> format_str, | |
bd9231e4 | 165 | typename detail::vformat_args<Char>::type args) { |
05aa7e19 JG |
166 | auto buffer = basic_memory_buffer<Char>(); |
167 | detail::vformat_to(buffer, format_str, args); | |
8b75cd77 | 168 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
05aa7e19 JG |
169 | detail::write_buffer(os, buffer); |
170 | } | |
171 | ||
172 | /** | |
bd9231e4 JG |
173 | * Prints formatted data to the stream `os`. |
174 | * | |
175 | * **Example**: | |
176 | * | |
177 | * fmt::print(cerr, "Don't {}!", "panic"); | |
05aa7e19 | 178 | */ |
bd9231e4 | 179 | FMT_EXPORT template <typename... T> |
8b75cd77 JG |
180 | void print(std::ostream& os, format_string<T...> fmt, T&&... args) { |
181 | const auto& vargs = fmt::make_format_args(args...); | |
bd9231e4 | 182 | if (detail::use_utf8()) |
8b75cd77 JG |
183 | vprint(os, fmt, vargs); |
184 | else | |
185 | detail::vprint_directly(os, fmt, vargs); | |
186 | } | |
187 | ||
bd9231e4 | 188 | FMT_EXPORT |
8b75cd77 JG |
189 | template <typename... Args> |
190 | void print(std::wostream& os, | |
191 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | |
192 | Args&&... args) { | |
bd9231e4 JG |
193 | vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...)); |
194 | } | |
195 | ||
196 | FMT_EXPORT template <typename... T> | |
197 | void println(std::ostream& os, format_string<T...> fmt, T&&... args) { | |
198 | fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); | |
199 | } | |
200 | ||
201 | FMT_EXPORT | |
202 | template <typename... Args> | |
203 | void println(std::wostream& os, | |
204 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | |
205 | Args&&... args) { | |
206 | print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...)); | |
05aa7e19 | 207 | } |
8b75cd77 | 208 | |
05aa7e19 JG |
209 | FMT_END_NAMESPACE |
210 | ||
211 | #endif // FMT_OSTREAM_H_ |