xref: /aosp_15_r20/external/fmtlib/include/fmt/std.h (revision 5c90c05cd622c0a81b57953a4d343e0e489f2e08)
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