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