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