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