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 | ||
8b75cd77 | 11 | #include <fstream> |
05aa7e19 | 12 | #include <ostream> |
8b75cd77 JG |
13 | #if defined(_WIN32) && defined(__GLIBCXX__) |
14 | # include <ext/stdio_filebuf.h> | |
15 | # include <ext/stdio_sync_filebuf.h> | |
16 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |
17 | # include <__std_stream> | |
18 | #endif | |
05aa7e19 JG |
19 | |
20 | #include "format.h" | |
21 | ||
22 | FMT_BEGIN_NAMESPACE | |
23 | ||
24 | template <typename OutputIt, typename Char> class basic_printf_context; | |
25 | ||
26 | namespace detail { | |
27 | ||
28 | // Checks if T has a user-defined operator<<. | |
29 | template <typename T, typename Char, typename Enable = void> | |
30 | class is_streamable { | |
31 | private: | |
32 | template <typename U> | |
33 | static auto test(int) | |
34 | -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() | |
35 | << std::declval<U>()) != 0>; | |
36 | ||
37 | template <typename> static auto test(...) -> std::false_type; | |
38 | ||
39 | using result = decltype(test<T>(0)); | |
40 | ||
41 | public: | |
42 | is_streamable() = default; | |
43 | ||
44 | static const bool value = result::value; | |
45 | }; | |
46 | ||
47 | // Formatting of built-in types and arrays is intentionally disabled because | |
48 | // it's handled by standard (non-ostream) formatters. | |
49 | template <typename T, typename Char> | |
50 | struct is_streamable< | |
51 | T, Char, | |
52 | enable_if_t< | |
53 | std::is_arithmetic<T>::value || std::is_array<T>::value || | |
54 | std::is_pointer<T>::value || std::is_same<T, char8_type>::value || | |
8b75cd77 | 55 | std::is_convertible<T, fmt::basic_string_view<Char>>::value || |
05aa7e19 JG |
56 | std::is_same<T, std_string_view<Char>>::value || |
57 | (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> | |
58 | : std::false_type {}; | |
59 | ||
8b75cd77 JG |
60 | // Generate a unique explicit instantion in every translation unit using a tag |
61 | // type in an anonymous namespace. | |
62 | namespace { | |
63 | struct file_access_tag {}; | |
64 | } // namespace | |
65 | template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> | |
66 | class file_access { | |
67 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } | |
68 | }; | |
69 | ||
70 | #if FMT_MSC_VERSION | |
71 | template class file_access<file_access_tag, std::filebuf, | |
72 | &std::filebuf::_Myfile>; | |
73 | auto get_file(std::filebuf&) -> FILE*; | |
74 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |
75 | template class file_access<file_access_tag, std::__stdoutbuf<char>, | |
76 | &std::__stdoutbuf<char>::__file_>; | |
77 | auto get_file(std::__stdoutbuf<char>&) -> FILE*; | |
78 | #endif | |
79 | ||
80 | inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { | |
81 | #if FMT_MSC_VERSION | |
82 | if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) | |
83 | if (FILE* f = get_file(*buf)) return write_console(f, data); | |
84 | #elif defined(_WIN32) && defined(__GLIBCXX__) | |
85 | auto* rdbuf = os.rdbuf(); | |
86 | FILE* c_file; | |
87 | if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) | |
88 | c_file = fbuf->file(); | |
89 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) | |
90 | c_file = fbuf->file(); | |
91 | else | |
92 | return false; | |
93 | if (c_file) return write_console(c_file, data); | |
94 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) | |
95 | if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) | |
96 | if (FILE* f = get_file(*buf)) return write_console(f, data); | |
97 | #else | |
98 | ignore_unused(os, data); | |
99 | #endif | |
100 | return false; | |
101 | } | |
102 | inline bool write_ostream_unicode(std::wostream&, | |
103 | fmt::basic_string_view<wchar_t>) { | |
104 | return false; | |
105 | } | |
106 | ||
05aa7e19 JG |
107 | // Write the content of buf to os. |
108 | // It is a separate function rather than a part of vprint to simplify testing. | |
109 | template <typename Char> | |
110 | void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { | |
111 | const Char* buf_data = buf.data(); | |
112 | using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; | |
113 | unsigned_streamsize size = buf.size(); | |
114 | unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); | |
115 | do { | |
116 | unsigned_streamsize n = size <= max_size ? size : max_size; | |
117 | os.write(buf_data, static_cast<std::streamsize>(n)); | |
118 | buf_data += n; | |
119 | size -= n; | |
120 | } while (size != 0); | |
121 | } | |
122 | ||
123 | template <typename Char, typename T> | |
124 | void format_value(buffer<Char>& buf, const T& value, | |
125 | locale_ref loc = locale_ref()) { | |
126 | auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); | |
127 | auto&& output = std::basic_ostream<Char>(&format_buf); | |
128 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) | |
129 | if (loc) output.imbue(loc.get<std::locale>()); | |
130 | #endif | |
131 | output << value; | |
132 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | |
05aa7e19 JG |
133 | } |
134 | ||
8b75cd77 JG |
135 | template <typename T> struct streamed_view { const T& value; }; |
136 | ||
137 | } // namespace detail | |
138 | ||
05aa7e19 | 139 | // Formats an object of type T that has an overloaded ostream operator<<. |
8b75cd77 JG |
140 | template <typename Char> |
141 | struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { | |
142 | void set_debug_format() = delete; | |
05aa7e19 | 143 | |
8b75cd77 JG |
144 | template <typename T, typename OutputIt> |
145 | auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const | |
05aa7e19 JG |
146 | -> OutputIt { |
147 | auto buffer = basic_memory_buffer<Char>(); | |
148 | format_value(buffer, value, ctx.locale()); | |
149 | return formatter<basic_string_view<Char>, Char>::format( | |
150 | {buffer.data(), buffer.size()}, ctx); | |
151 | } | |
8b75cd77 JG |
152 | }; |
153 | ||
154 | using ostream_formatter = basic_ostream_formatter<char>; | |
05aa7e19 | 155 | |
8b75cd77 JG |
156 | template <typename T, typename Char> |
157 | struct formatter<detail::streamed_view<T>, Char> | |
158 | : basic_ostream_formatter<Char> { | |
05aa7e19 | 159 | template <typename OutputIt> |
8b75cd77 JG |
160 | auto format(detail::streamed_view<T> view, |
161 | basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { | |
162 | return basic_ostream_formatter<Char>::format(view.value, ctx); | |
05aa7e19 JG |
163 | } |
164 | }; | |
8b75cd77 JG |
165 | |
166 | /** | |
167 | \rst | |
168 | Returns a view that formats `value` via an ostream ``operator<<``. | |
169 | ||
170 | **Example**:: | |
171 | ||
172 | fmt::print("Current thread id: {}\n", | |
173 | fmt::streamed(std::this_thread::get_id())); | |
174 | \endrst | |
175 | */ | |
176 | template <typename T> | |
177 | auto streamed(const T& value) -> detail::streamed_view<T> { | |
178 | return {value}; | |
179 | } | |
180 | ||
181 | namespace detail { | |
182 | ||
183 | // Formats an object of type T that has an overloaded ostream operator<<. | |
184 | template <typename T, typename Char> | |
185 | struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> | |
186 | : basic_ostream_formatter<Char> { | |
187 | using basic_ostream_formatter<Char>::format; | |
188 | }; | |
189 | ||
190 | inline void vprint_directly(std::ostream& os, string_view format_str, | |
191 | format_args args) { | |
192 | auto buffer = memory_buffer(); | |
193 | detail::vformat_to(buffer, format_str, args); | |
194 | detail::write_buffer(os, buffer); | |
195 | } | |
196 | ||
05aa7e19 JG |
197 | } // namespace detail |
198 | ||
8b75cd77 JG |
199 | FMT_MODULE_EXPORT template <typename Char> |
200 | void vprint(std::basic_ostream<Char>& os, | |
201 | basic_string_view<type_identity_t<Char>> format_str, | |
05aa7e19 JG |
202 | basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
203 | auto buffer = basic_memory_buffer<Char>(); | |
204 | detail::vformat_to(buffer, format_str, args); | |
8b75cd77 | 205 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
05aa7e19 JG |
206 | detail::write_buffer(os, buffer); |
207 | } | |
208 | ||
209 | /** | |
210 | \rst | |
211 | Prints formatted data to the stream *os*. | |
212 | ||
213 | **Example**:: | |
214 | ||
215 | fmt::print(cerr, "Don't {}!", "panic"); | |
216 | \endrst | |
217 | */ | |
8b75cd77 JG |
218 | FMT_MODULE_EXPORT template <typename... T> |
219 | void print(std::ostream& os, format_string<T...> fmt, T&&... args) { | |
220 | const auto& vargs = fmt::make_format_args(args...); | |
221 | if (detail::is_utf8()) | |
222 | vprint(os, fmt, vargs); | |
223 | else | |
224 | detail::vprint_directly(os, fmt, vargs); | |
225 | } | |
226 | ||
05aa7e19 | 227 | FMT_MODULE_EXPORT |
8b75cd77 JG |
228 | template <typename... Args> |
229 | void print(std::wostream& os, | |
230 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | |
231 | Args&&... args) { | |
232 | vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); | |
05aa7e19 | 233 | } |
8b75cd77 | 234 | |
05aa7e19 JG |
235 | FMT_END_NAMESPACE |
236 | ||
237 | #endif // FMT_OSTREAM_H_ |