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