Commit | Line | Data |
---|---|---|
8b75cd77 JG |
1 | // Formatting library for C++ - formatters for standard library types |
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_STD_H_ | |
9 | #define FMT_STD_H_ | |
10 | ||
bd9231e4 | 11 | #include "format.h" |
8b75cd77 JG |
12 | #include "ostream.h" |
13 | ||
bd9231e4 JG |
14 | #ifndef FMT_MODULE |
15 | # include <atomic> | |
16 | # include <bitset> | |
17 | # include <complex> | |
18 | # include <cstdlib> | |
19 | # include <exception> | |
20 | # include <memory> | |
21 | # include <thread> | |
22 | # include <type_traits> | |
23 | # include <typeinfo> | |
24 | # include <utility> | |
25 | # include <vector> | |
26 | ||
27 | // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. | |
28 | # if FMT_CPLUSPLUS >= 201703L | |
29 | # if FMT_HAS_INCLUDE(<filesystem>) | |
30 | # include <filesystem> | |
31 | # endif | |
32 | # if FMT_HAS_INCLUDE(<variant>) | |
33 | # include <variant> | |
34 | # endif | |
35 | # if FMT_HAS_INCLUDE(<optional>) | |
36 | # include <optional> | |
37 | # endif | |
38 | # endif | |
39 | // Use > instead of >= in the version check because <source_location> may be | |
40 | // available after C++17 but before C++20 is marked as implemented. | |
41 | # if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>) | |
42 | # include <source_location> | |
43 | # endif | |
44 | # if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>) | |
45 | # include <expected> | |
46 | # endif | |
47 | #endif // FMT_MODULE | |
48 | ||
8b75cd77 JG |
49 | #if FMT_HAS_INCLUDE(<version>) |
50 | # include <version> | |
51 | #endif | |
bd9231e4 JG |
52 | |
53 | // GCC 4 does not support FMT_HAS_INCLUDE. | |
54 | #if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__) | |
55 | # include <cxxabi.h> | |
56 | // Android NDK with gabi++ library on some architectures does not implement | |
57 | // abi::__cxa_demangle(). | |
58 | # ifndef __GABIXX_CXXABI_H__ | |
59 | # define FMT_HAS_ABI_CXA_DEMANGLE | |
60 | # endif | |
61 | #endif | |
62 | ||
63 | // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. | |
64 | #ifndef FMT_CPP_LIB_FILESYSTEM | |
65 | # ifdef __cpp_lib_filesystem | |
66 | # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem | |
67 | # else | |
68 | # define FMT_CPP_LIB_FILESYSTEM 0 | |
8b75cd77 | 69 | # endif |
bd9231e4 JG |
70 | #endif |
71 | ||
72 | #ifndef FMT_CPP_LIB_VARIANT | |
73 | # ifdef __cpp_lib_variant | |
74 | # define FMT_CPP_LIB_VARIANT __cpp_lib_variant | |
75 | # else | |
76 | # define FMT_CPP_LIB_VARIANT 0 | |
8b75cd77 JG |
77 | # endif |
78 | #endif | |
79 | ||
bd9231e4 | 80 | #if FMT_CPP_LIB_FILESYSTEM |
8b75cd77 JG |
81 | FMT_BEGIN_NAMESPACE |
82 | ||
83 | namespace detail { | |
84 | ||
bd9231e4 JG |
85 | template <typename Char, typename PathChar> |
86 | auto get_path_string(const std::filesystem::path& p, | |
87 | const std::basic_string<PathChar>& native) { | |
88 | if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>) | |
89 | return to_utf8<wchar_t>(native, to_utf8_error_policy::replace); | |
90 | else | |
91 | return p.string<Char>(); | |
8b75cd77 | 92 | } |
bd9231e4 JG |
93 | |
94 | template <typename Char, typename PathChar> | |
95 | void write_escaped_path(basic_memory_buffer<Char>& quoted, | |
96 | const std::filesystem::path& p, | |
97 | const std::basic_string<PathChar>& native) { | |
98 | if constexpr (std::is_same_v<Char, char> && | |
99 | std::is_same_v<PathChar, wchar_t>) { | |
100 | auto buf = basic_memory_buffer<wchar_t>(); | |
101 | write_escaped_string<wchar_t>(std::back_inserter(buf), native); | |
102 | bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}); | |
103 | FMT_ASSERT(valid, "invalid utf16"); | |
104 | } else if constexpr (std::is_same_v<Char, PathChar>) { | |
105 | write_escaped_string<std::filesystem::path::value_type>( | |
106 | std::back_inserter(quoted), native); | |
107 | } else { | |
108 | write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>()); | |
109 | } | |
8b75cd77 JG |
110 | } |
111 | ||
112 | } // namespace detail | |
113 | ||
bd9231e4 JG |
114 | FMT_EXPORT |
115 | template <typename Char> struct formatter<std::filesystem::path, Char> { | |
116 | private: | |
117 | format_specs specs_; | |
118 | detail::arg_ref<Char> width_ref_; | |
119 | bool debug_ = false; | |
120 | char path_type_ = 0; | |
121 | ||
122 | public: | |
123 | FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } | |
124 | ||
125 | template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | |
126 | auto it = ctx.begin(), end = ctx.end(); | |
127 | if (it == end) return it; | |
128 | ||
129 | it = detail::parse_align(it, end, specs_); | |
130 | if (it == end) return it; | |
131 | ||
132 | it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); | |
133 | if (it != end && *it == '?') { | |
134 | debug_ = true; | |
135 | ++it; | |
136 | } | |
137 | if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); | |
138 | return it; | |
139 | } | |
140 | ||
8b75cd77 | 141 | template <typename FormatContext> |
bd9231e4 JG |
142 | auto format(const std::filesystem::path& p, FormatContext& ctx) const { |
143 | auto specs = specs_; | |
144 | auto path_string = | |
145 | !path_type_ ? p.native() | |
146 | : p.generic_string<std::filesystem::path::value_type>(); | |
147 | ||
148 | detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_, | |
149 | ctx); | |
150 | if (!debug_) { | |
151 | auto s = detail::get_path_string<Char>(p, path_string); | |
152 | return detail::write(ctx.out(), basic_string_view<Char>(s), specs); | |
153 | } | |
154 | auto quoted = basic_memory_buffer<Char>(); | |
155 | detail::write_escaped_path(quoted, p, path_string); | |
156 | return detail::write(ctx.out(), | |
157 | basic_string_view<Char>(quoted.data(), quoted.size()), | |
158 | specs); | |
8b75cd77 JG |
159 | } |
160 | }; | |
bd9231e4 JG |
161 | |
162 | class path : public std::filesystem::path { | |
163 | public: | |
164 | auto display_string() const -> std::string { | |
165 | const std::filesystem::path& base = *this; | |
166 | return fmt::format(FMT_STRING("{}"), base); | |
167 | } | |
168 | auto system_string() const -> std::string { return string(); } | |
169 | ||
170 | auto generic_display_string() const -> std::string { | |
171 | const std::filesystem::path& base = *this; | |
172 | return fmt::format(FMT_STRING("{:g}"), base); | |
173 | } | |
174 | auto generic_system_string() const -> std::string { return generic_string(); } | |
175 | }; | |
176 | ||
8b75cd77 | 177 | FMT_END_NAMESPACE |
bd9231e4 | 178 | #endif // FMT_CPP_LIB_FILESYSTEM |
8b75cd77 JG |
179 | |
180 | FMT_BEGIN_NAMESPACE | |
bd9231e4 JG |
181 | FMT_EXPORT |
182 | template <std::size_t N, typename Char> | |
183 | struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> { | |
184 | private: | |
185 | // Functor because C++11 doesn't support generic lambdas. | |
186 | struct writer { | |
187 | const std::bitset<N>& bs; | |
188 | ||
189 | template <typename OutputIt> | |
190 | FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { | |
191 | for (auto pos = N; pos > 0; --pos) { | |
192 | out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0')); | |
193 | } | |
194 | ||
195 | return out; | |
196 | } | |
197 | }; | |
198 | ||
199 | public: | |
200 | template <typename FormatContext> | |
201 | auto format(const std::bitset<N>& bs, FormatContext& ctx) const | |
202 | -> decltype(ctx.out()) { | |
203 | return write_padded(ctx, writer{bs}); | |
204 | } | |
205 | }; | |
206 | ||
207 | FMT_EXPORT | |
8b75cd77 JG |
208 | template <typename Char> |
209 | struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; | |
210 | FMT_END_NAMESPACE | |
211 | ||
bd9231e4 | 212 | #ifdef __cpp_lib_optional |
8b75cd77 | 213 | FMT_BEGIN_NAMESPACE |
bd9231e4 JG |
214 | FMT_EXPORT |
215 | template <typename T, typename Char> | |
216 | struct formatter<std::optional<T>, Char, | |
217 | std::enable_if_t<is_formattable<T, Char>::value>> { | |
218 | private: | |
219 | formatter<T, Char> underlying_; | |
220 | static constexpr basic_string_view<Char> optional = | |
221 | detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l', | |
222 | '('>{}; | |
223 | static constexpr basic_string_view<Char> none = | |
224 | detail::string_literal<Char, 'n', 'o', 'n', 'e'>{}; | |
225 | ||
226 | template <class U> | |
227 | FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set) | |
228 | -> decltype(u.set_debug_format(set)) { | |
229 | u.set_debug_format(set); | |
230 | } | |
231 | ||
232 | template <class U> | |
233 | FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} | |
234 | ||
235 | public: | |
236 | template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | |
237 | maybe_set_debug_format(underlying_, true); | |
238 | return underlying_.parse(ctx); | |
239 | } | |
240 | ||
241 | template <typename FormatContext> | |
242 | auto format(const std::optional<T>& opt, FormatContext& ctx) const | |
243 | -> decltype(ctx.out()) { | |
244 | if (!opt) return detail::write<Char>(ctx.out(), none); | |
245 | ||
246 | auto out = ctx.out(); | |
247 | out = detail::write<Char>(out, optional); | |
248 | ctx.advance_to(out); | |
249 | out = underlying_.format(*opt, ctx); | |
250 | return detail::write(out, ')'); | |
251 | } | |
252 | }; | |
253 | FMT_END_NAMESPACE | |
254 | #endif // __cpp_lib_optional | |
255 | ||
256 | #if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT | |
257 | ||
258 | FMT_BEGIN_NAMESPACE | |
259 | namespace detail { | |
260 | ||
261 | template <typename Char, typename OutputIt, typename T> | |
262 | auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { | |
263 | if constexpr (has_to_string_view<T>::value) | |
264 | return write_escaped_string<Char>(out, detail::to_string_view(v)); | |
265 | if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v); | |
266 | return write<Char>(out, v); | |
267 | } | |
268 | ||
269 | } // namespace detail | |
270 | ||
271 | FMT_END_NAMESPACE | |
272 | #endif | |
273 | ||
274 | #ifdef __cpp_lib_expected | |
275 | FMT_BEGIN_NAMESPACE | |
276 | ||
277 | FMT_EXPORT | |
278 | template <typename T, typename E, typename Char> | |
279 | struct formatter<std::expected<T, E>, Char, | |
280 | std::enable_if_t<is_formattable<T, Char>::value && | |
281 | is_formattable<E, Char>::value>> { | |
8b75cd77 JG |
282 | template <typename ParseContext> |
283 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
284 | return ctx.begin(); | |
285 | } | |
286 | ||
287 | template <typename FormatContext> | |
bd9231e4 JG |
288 | auto format(const std::expected<T, E>& value, FormatContext& ctx) const |
289 | -> decltype(ctx.out()) { | |
290 | auto out = ctx.out(); | |
291 | ||
292 | if (value.has_value()) { | |
293 | out = detail::write<Char>(out, "expected("); | |
294 | out = detail::write_escaped_alternative<Char>(out, *value); | |
295 | } else { | |
296 | out = detail::write<Char>(out, "unexpected("); | |
297 | out = detail::write_escaped_alternative<Char>(out, value.error()); | |
298 | } | |
299 | *out++ = ')'; | |
300 | return out; | |
301 | } | |
302 | }; | |
303 | FMT_END_NAMESPACE | |
304 | #endif // __cpp_lib_expected | |
305 | ||
306 | #ifdef __cpp_lib_source_location | |
307 | FMT_BEGIN_NAMESPACE | |
308 | FMT_EXPORT | |
309 | template <> struct formatter<std::source_location> { | |
310 | template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | |
311 | return ctx.begin(); | |
312 | } | |
313 | ||
314 | template <typename FormatContext> | |
315 | auto format(const std::source_location& loc, FormatContext& ctx) const | |
8b75cd77 JG |
316 | -> decltype(ctx.out()) { |
317 | auto out = ctx.out(); | |
bd9231e4 JG |
318 | out = detail::write(out, loc.file_name()); |
319 | out = detail::write(out, ':'); | |
320 | out = detail::write<char>(out, loc.line()); | |
321 | out = detail::write(out, ':'); | |
322 | out = detail::write<char>(out, loc.column()); | |
323 | out = detail::write(out, ": "); | |
324 | out = detail::write(out, loc.function_name()); | |
8b75cd77 JG |
325 | return out; |
326 | } | |
327 | }; | |
bd9231e4 JG |
328 | FMT_END_NAMESPACE |
329 | #endif | |
8b75cd77 | 330 | |
bd9231e4 JG |
331 | #if FMT_CPP_LIB_VARIANT |
332 | FMT_BEGIN_NAMESPACE | |
8b75cd77 JG |
333 | namespace detail { |
334 | ||
335 | template <typename T> | |
336 | using variant_index_sequence = | |
337 | std::make_index_sequence<std::variant_size<T>::value>; | |
338 | ||
bd9231e4 JG |
339 | template <typename> struct is_variant_like_ : std::false_type {}; |
340 | template <typename... Types> | |
341 | struct is_variant_like_<std::variant<Types...>> : std::true_type {}; | |
8b75cd77 | 342 | |
bd9231e4 | 343 | // formattable element check. |
8b75cd77 | 344 | template <typename T, typename C> class is_variant_formattable_ { |
bd9231e4 | 345 | template <std::size_t... Is> |
8b75cd77 | 346 | static std::conjunction< |
bd9231e4 JG |
347 | is_formattable<std::variant_alternative_t<Is, T>, C>...> |
348 | check(std::index_sequence<Is...>); | |
8b75cd77 JG |
349 | |
350 | public: | |
351 | static constexpr const bool value = | |
352 | decltype(check(variant_index_sequence<T>{}))::value; | |
353 | }; | |
354 | ||
8b75cd77 JG |
355 | } // namespace detail |
356 | ||
357 | template <typename T> struct is_variant_like { | |
358 | static constexpr const bool value = detail::is_variant_like_<T>::value; | |
359 | }; | |
360 | ||
361 | template <typename T, typename C> struct is_variant_formattable { | |
362 | static constexpr const bool value = | |
363 | detail::is_variant_formattable_<T, C>::value; | |
364 | }; | |
365 | ||
bd9231e4 JG |
366 | FMT_EXPORT |
367 | template <typename Char> struct formatter<std::monostate, Char> { | |
368 | template <typename ParseContext> | |
369 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
370 | return ctx.begin(); | |
371 | } | |
372 | ||
373 | template <typename FormatContext> | |
374 | auto format(const std::monostate&, FormatContext& ctx) const | |
375 | -> decltype(ctx.out()) { | |
376 | return detail::write<Char>(ctx.out(), "monostate"); | |
377 | } | |
378 | }; | |
379 | ||
380 | FMT_EXPORT | |
8b75cd77 JG |
381 | template <typename Variant, typename Char> |
382 | struct formatter< | |
383 | Variant, Char, | |
384 | std::enable_if_t<std::conjunction_v< | |
385 | is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { | |
386 | template <typename ParseContext> | |
387 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
388 | return ctx.begin(); | |
389 | } | |
390 | ||
391 | template <typename FormatContext> | |
392 | auto format(const Variant& value, FormatContext& ctx) const | |
393 | -> decltype(ctx.out()) { | |
394 | auto out = ctx.out(); | |
395 | ||
396 | out = detail::write<Char>(out, "variant("); | |
bd9231e4 JG |
397 | FMT_TRY { |
398 | std::visit( | |
399 | [&](const auto& v) { | |
400 | out = detail::write_escaped_alternative<Char>(out, v); | |
401 | }, | |
402 | value); | |
403 | } | |
404 | FMT_CATCH(const std::bad_variant_access&) { | |
405 | detail::write<Char>(out, "valueless by exception"); | |
406 | } | |
8b75cd77 JG |
407 | *out++ = ')'; |
408 | return out; | |
409 | } | |
410 | }; | |
411 | FMT_END_NAMESPACE | |
bd9231e4 JG |
412 | #endif // FMT_CPP_LIB_VARIANT |
413 | ||
414 | FMT_BEGIN_NAMESPACE | |
415 | FMT_EXPORT | |
416 | template <typename Char> struct formatter<std::error_code, Char> { | |
417 | template <typename ParseContext> | |
418 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | |
419 | return ctx.begin(); | |
420 | } | |
421 | ||
422 | template <typename FormatContext> | |
423 | FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const | |
424 | -> decltype(ctx.out()) { | |
425 | auto out = ctx.out(); | |
426 | out = detail::write_bytes<Char>(out, ec.category().name(), format_specs()); | |
427 | out = detail::write<Char>(out, Char(':')); | |
428 | out = detail::write<Char>(out, ec.value()); | |
429 | return out; | |
430 | } | |
431 | }; | |
432 | ||
433 | #if FMT_USE_RTTI | |
434 | namespace detail { | |
435 | ||
436 | template <typename Char, typename OutputIt> | |
437 | auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { | |
438 | # ifdef FMT_HAS_ABI_CXA_DEMANGLE | |
439 | int status = 0; | |
440 | std::size_t size = 0; | |
441 | std::unique_ptr<char, void (*)(void*)> demangled_name_ptr( | |
442 | abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); | |
443 | ||
444 | string_view demangled_name_view; | |
445 | if (demangled_name_ptr) { | |
446 | demangled_name_view = demangled_name_ptr.get(); | |
447 | ||
448 | // Normalization of stdlib inline namespace names. | |
449 | // libc++ inline namespaces. | |
450 | // std::__1::* -> std::* | |
451 | // std::__1::__fs::* -> std::* | |
452 | // libstdc++ inline namespaces. | |
453 | // std::__cxx11::* -> std::* | |
454 | // std::filesystem::__cxx11::* -> std::filesystem::* | |
455 | if (demangled_name_view.starts_with("std::")) { | |
456 | char* begin = demangled_name_ptr.get(); | |
457 | char* to = begin + 5; // std:: | |
458 | for (char *from = to, *end = begin + demangled_name_view.size(); | |
459 | from < end;) { | |
460 | // This is safe, because demangled_name is NUL-terminated. | |
461 | if (from[0] == '_' && from[1] == '_') { | |
462 | char* next = from + 1; | |
463 | while (next < end && *next != ':') next++; | |
464 | if (next[0] == ':' && next[1] == ':') { | |
465 | from = next + 2; | |
466 | continue; | |
467 | } | |
468 | } | |
469 | *to++ = *from++; | |
470 | } | |
471 | demangled_name_view = {begin, detail::to_unsigned(to - begin)}; | |
472 | } | |
473 | } else { | |
474 | demangled_name_view = string_view(ti.name()); | |
475 | } | |
476 | return detail::write_bytes<Char>(out, demangled_name_view); | |
477 | # elif FMT_MSC_VERSION | |
478 | const string_view demangled_name(ti.name()); | |
479 | for (std::size_t i = 0; i < demangled_name.size(); ++i) { | |
480 | auto sub = demangled_name; | |
481 | sub.remove_prefix(i); | |
482 | if (sub.starts_with("enum ")) { | |
483 | i += 4; | |
484 | continue; | |
485 | } | |
486 | if (sub.starts_with("class ") || sub.starts_with("union ")) { | |
487 | i += 5; | |
488 | continue; | |
489 | } | |
490 | if (sub.starts_with("struct ")) { | |
491 | i += 6; | |
492 | continue; | |
493 | } | |
494 | if (*sub.begin() != ' ') *out++ = *sub.begin(); | |
495 | } | |
496 | return out; | |
497 | # else | |
498 | return detail::write_bytes<Char>(out, string_view(ti.name())); | |
499 | # endif | |
500 | } | |
501 | ||
502 | } // namespace detail | |
503 | ||
504 | FMT_EXPORT | |
505 | template <typename Char> | |
506 | struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types. | |
507 | > { | |
508 | public: | |
509 | FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) | |
510 | -> decltype(ctx.begin()) { | |
511 | return ctx.begin(); | |
512 | } | |
513 | ||
514 | template <typename Context> | |
515 | auto format(const std::type_info& ti, Context& ctx) const | |
516 | -> decltype(ctx.out()) { | |
517 | return detail::write_demangled_name<Char>(ctx.out(), ti); | |
518 | } | |
519 | }; | |
8b75cd77 JG |
520 | #endif |
521 | ||
bd9231e4 JG |
522 | FMT_EXPORT |
523 | template <typename T, typename Char> | |
524 | struct formatter< | |
525 | T, Char, // DEPRECATED! Mixing code unit types. | |
526 | typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> { | |
527 | private: | |
528 | bool with_typename_ = false; | |
529 | ||
530 | public: | |
531 | FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) | |
532 | -> decltype(ctx.begin()) { | |
533 | auto it = ctx.begin(); | |
534 | auto end = ctx.end(); | |
535 | if (it == end || *it == '}') return it; | |
536 | if (*it == 't') { | |
537 | ++it; | |
538 | with_typename_ = FMT_USE_RTTI != 0; | |
539 | } | |
540 | return it; | |
541 | } | |
542 | ||
543 | template <typename Context> | |
544 | auto format(const std::exception& ex, Context& ctx) const | |
545 | -> decltype(ctx.out()) { | |
546 | auto out = ctx.out(); | |
547 | #if FMT_USE_RTTI | |
548 | if (with_typename_) { | |
549 | out = detail::write_demangled_name<Char>(out, typeid(ex)); | |
550 | *out++ = ':'; | |
551 | *out++ = ' '; | |
552 | } | |
553 | #endif | |
554 | return detail::write_bytes<Char>(out, string_view(ex.what())); | |
555 | } | |
556 | }; | |
557 | ||
558 | namespace detail { | |
559 | ||
560 | template <typename T, typename Enable = void> | |
561 | struct has_flip : std::false_type {}; | |
562 | ||
563 | template <typename T> | |
564 | struct has_flip<T, void_t<decltype(std::declval<T>().flip())>> | |
565 | : std::true_type {}; | |
566 | ||
567 | template <typename T> struct is_bit_reference_like { | |
568 | static constexpr const bool value = | |
569 | std::is_convertible<T, bool>::value && | |
570 | std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value; | |
571 | }; | |
572 | ||
573 | #ifdef _LIBCPP_VERSION | |
574 | ||
575 | // Workaround for libc++ incompatibility with C++ standard. | |
576 | // According to the Standard, `bitset::operator[] const` returns bool. | |
577 | template <typename C> | |
578 | struct is_bit_reference_like<std::__bit_const_reference<C>> { | |
579 | static constexpr const bool value = true; | |
580 | }; | |
581 | ||
582 | #endif | |
583 | ||
584 | } // namespace detail | |
585 | ||
586 | // We can't use std::vector<bool, Allocator>::reference and | |
587 | // std::bitset<N>::reference because the compiler can't deduce Allocator and N | |
588 | // in partial specialization. | |
589 | FMT_EXPORT | |
590 | template <typename BitRef, typename Char> | |
591 | struct formatter<BitRef, Char, | |
592 | enable_if_t<detail::is_bit_reference_like<BitRef>::value>> | |
593 | : formatter<bool, Char> { | |
594 | template <typename FormatContext> | |
595 | FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const | |
596 | -> decltype(ctx.out()) { | |
597 | return formatter<bool, Char>::format(v, ctx); | |
598 | } | |
599 | }; | |
600 | ||
601 | template <typename T, typename Deleter> | |
602 | auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* { | |
603 | return p.get(); | |
604 | } | |
605 | template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* { | |
606 | return p.get(); | |
607 | } | |
608 | ||
609 | FMT_EXPORT | |
610 | template <typename T, typename Char> | |
611 | struct formatter<std::atomic<T>, Char, | |
612 | enable_if_t<is_formattable<T, Char>::value>> | |
613 | : formatter<T, Char> { | |
614 | template <typename FormatContext> | |
615 | auto format(const std::atomic<T>& v, FormatContext& ctx) const | |
616 | -> decltype(ctx.out()) { | |
617 | return formatter<T, Char>::format(v.load(), ctx); | |
618 | } | |
619 | }; | |
620 | ||
621 | #ifdef __cpp_lib_atomic_flag_test | |
622 | FMT_EXPORT | |
623 | template <typename Char> | |
624 | struct formatter<std::atomic_flag, Char> : formatter<bool, Char> { | |
625 | template <typename FormatContext> | |
626 | auto format(const std::atomic_flag& v, FormatContext& ctx) const | |
627 | -> decltype(ctx.out()) { | |
628 | return formatter<bool, Char>::format(v.test(), ctx); | |
629 | } | |
630 | }; | |
631 | #endif // __cpp_lib_atomic_flag_test | |
632 | ||
633 | FMT_EXPORT | |
634 | template <typename T, typename Char> struct formatter<std::complex<T>, Char> { | |
635 | private: | |
636 | detail::dynamic_format_specs<Char> specs_; | |
637 | ||
638 | template <typename FormatContext, typename OutputIt> | |
639 | FMT_CONSTEXPR auto do_format(const std::complex<T>& c, | |
640 | detail::dynamic_format_specs<Char>& specs, | |
641 | FormatContext& ctx, OutputIt out) const | |
642 | -> OutputIt { | |
643 | if (c.real() != 0) { | |
644 | *out++ = Char('('); | |
645 | out = detail::write<Char>(out, c.real(), specs, ctx.locale()); | |
646 | specs.sign = sign::plus; | |
647 | out = detail::write<Char>(out, c.imag(), specs, ctx.locale()); | |
648 | if (!detail::isfinite(c.imag())) *out++ = Char(' '); | |
649 | *out++ = Char('i'); | |
650 | *out++ = Char(')'); | |
651 | return out; | |
652 | } | |
653 | out = detail::write<Char>(out, c.imag(), specs, ctx.locale()); | |
654 | if (!detail::isfinite(c.imag())) *out++ = Char(' '); | |
655 | *out++ = Char('i'); | |
656 | return out; | |
657 | } | |
658 | ||
659 | public: | |
660 | FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) | |
661 | -> decltype(ctx.begin()) { | |
662 | if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); | |
663 | return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, | |
664 | detail::type_constant<T, Char>::value); | |
665 | } | |
666 | ||
667 | template <typename FormatContext> | |
668 | auto format(const std::complex<T>& c, FormatContext& ctx) const | |
669 | -> decltype(ctx.out()) { | |
670 | auto specs = specs_; | |
671 | if (specs.width_ref.kind != detail::arg_id_kind::none || | |
672 | specs.precision_ref.kind != detail::arg_id_kind::none) { | |
673 | detail::handle_dynamic_spec<detail::width_checker>(specs.width, | |
674 | specs.width_ref, ctx); | |
675 | detail::handle_dynamic_spec<detail::precision_checker>( | |
676 | specs.precision, specs.precision_ref, ctx); | |
677 | } | |
678 | ||
679 | if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); | |
680 | auto buf = basic_memory_buffer<Char>(); | |
681 | ||
682 | auto outer_specs = format_specs(); | |
683 | outer_specs.width = specs.width; | |
684 | outer_specs.fill = specs.fill; | |
685 | outer_specs.align = specs.align; | |
686 | ||
687 | specs.width = 0; | |
688 | specs.fill = {}; | |
689 | specs.align = align::none; | |
690 | ||
691 | do_format(c, specs, ctx, basic_appender<Char>(buf)); | |
692 | return detail::write<Char>(ctx.out(), | |
693 | basic_string_view<Char>(buf.data(), buf.size()), | |
694 | outer_specs); | |
695 | } | |
696 | }; | |
697 | ||
698 | FMT_END_NAMESPACE | |
8b75cd77 | 699 | #endif // FMT_STD_H_ |