quill
ranges.h
1 // Formatting library for C++ - range and tuple support
2 //
3 // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMTQUILL_RANGES_H_
9 #define FMTQUILL_RANGES_H_
10 
11 #ifndef FMTQUILL_MODULE
12 # include <initializer_list>
13 # include <iterator>
14 # include <tuple>
15 # include <type_traits>
16 # include <utility>
17 #endif
18 
19 #include "format.h"
20 
21 #if FMTQUILL_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
22 # define FMTQUILL_LIFETIMEBOUND [[clang::lifetimebound]]
23 #else
24 # define FMTQUILL_LIFETIMEBOUND
25 #endif
26 FMTQUILL_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address")
27 
28 FMTQUILL_BEGIN_NAMESPACE
29 
30 FMTQUILL_EXPORT
31 enum class range_format { disabled, map, set, sequence, string, debug_string };
32 
33 namespace detail {
34 
35 template <typename T> class is_map {
36  template <typename U> static auto check(U*) -> typename U::mapped_type;
37  template <typename> static void check(...);
38 
39  public:
40  static constexpr bool value =
41  !std::is_void<decltype(check<T>(nullptr))>::value;
42 };
43 
44 template <typename T> class is_set {
45  template <typename U> static auto check(U*) -> typename U::key_type;
46  template <typename> static void check(...);
47 
48  public:
49  static constexpr bool value =
50  !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
51 };
52 
53 // C array overload
54 template <typename T, size_t N>
55 auto range_begin(const T (&arr)[N]) -> const T* {
56  return arr;
57 }
58 template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
59  return arr + N;
60 }
61 
62 template <typename T, typename Enable = void>
63 struct has_member_fn_begin_end_t : std::false_type {};
64 
65 template <typename T>
66 struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
67  decltype(std::declval<T>().end())>>
68  : std::true_type {};
69 
70 // Member function overloads.
71 template <typename T>
72 auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
73  return static_cast<T&&>(rng).begin();
74 }
75 template <typename T>
76 auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
77  return static_cast<T&&>(rng).end();
78 }
79 
80 // ADL overloads. Only participate in overload resolution if member functions
81 // are not found.
82 template <typename T>
83 auto range_begin(T&& rng)
84  -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
85  decltype(begin(static_cast<T&&>(rng)))> {
86  return begin(static_cast<T&&>(rng));
87 }
88 template <typename T>
89 auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
90  decltype(end(static_cast<T&&>(rng)))> {
91  return end(static_cast<T&&>(rng));
92 }
93 
94 template <typename T, typename Enable = void>
95 struct has_const_begin_end : std::false_type {};
96 template <typename T, typename Enable = void>
97 struct has_mutable_begin_end : std::false_type {};
98 
99 template <typename T>
101  T, void_t<decltype(*detail::range_begin(
102  std::declval<const remove_cvref_t<T>&>())),
103  decltype(detail::range_end(
104  std::declval<const remove_cvref_t<T>&>()))>>
105  : std::true_type {};
106 
107 template <typename T>
109  T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
110  decltype(detail::range_end(std::declval<T&>())),
111  // the extra int here is because older versions of MSVC don't
112  // SFINAE properly unless there are distinct types
113  int>> : std::true_type {};
114 
115 template <typename T, typename _ = void> struct is_range_ : std::false_type {};
116 template <typename T>
117 struct is_range_<T, void>
118  : std::integral_constant<bool, (has_const_begin_end<T>::value ||
119  has_mutable_begin_end<T>::value)> {};
120 
121 // tuple_size and tuple_element check.
122 template <typename T> class is_tuple_like_ {
123  template <typename U, typename V = typename std::remove_cv<U>::type>
124  static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
125  template <typename> static void check(...);
126 
127  public:
128  static constexpr bool value =
129  !std::is_void<decltype(check<T>(nullptr))>::value;
130 };
131 
132 // Check for integer_sequence
133 #if defined(__cpp_lib_integer_sequence) || FMTQUILL_MSC_VERSION >= 1900
134 template <typename T, T... N>
135 using integer_sequence = std::integer_sequence<T, N...>;
136 template <size_t... N> using index_sequence = std::index_sequence<N...>;
137 template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
138 #else
139 template <typename T, T... N> struct integer_sequence {
140  using value_type = T;
141 
142  static FMTQUILL_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
143 };
144 
145 template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
146 
147 template <typename T, size_t N, T... Ns>
148 struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
149 template <typename T, T... Ns>
150 struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
151 
152 template <size_t N>
153 using make_index_sequence = make_integer_sequence<size_t, N>;
154 #endif
155 
156 template <typename T>
158 
159 template <typename T, typename C, bool = is_tuple_like_<T>::value>
161  public:
162  static constexpr bool value = false;
163 };
164 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
165  template <size_t... Is>
166  static auto all_true(index_sequence<Is...>,
167  integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
168  static auto all_true(...) -> std::false_type;
169 
170  template <size_t... Is>
171  static auto check(index_sequence<Is...>) -> decltype(all_true(
173  integer_sequence<bool,
174  (is_formattable<typename std::tuple_element<Is, T>::type,
175  C>::value)...>{}));
176 
177  public:
178  static constexpr bool value =
179  decltype(check(tuple_index_sequence<T>{}))::value;
180 };
181 
182 template <typename Tuple, typename F, size_t... Is>
183 FMTQUILL_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
184  using std::get;
185  // Using a free function get<Is>(Tuple) now.
186  const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
187  ignore_unused(unused);
188 }
189 
190 template <typename Tuple, typename F>
191 FMTQUILL_CONSTEXPR void for_each(Tuple&& t, F&& f) {
192  for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
193  std::forward<Tuple>(t), std::forward<F>(f));
194 }
195 
196 template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
197 void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
198  using std::get;
199  const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
200  ignore_unused(unused);
201 }
202 
203 template <typename Tuple1, typename Tuple2, typename F>
204 void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
205  for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
206  std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
207  std::forward<F>(f));
208 }
209 
210 namespace tuple {
211 // Workaround a bug in MSVC 2019 (v140).
212 template <typename Char, typename... T>
213 using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
214 
215 using std::get;
216 template <typename Tuple, typename Char, size_t... Is>
217 auto get_formatters(index_sequence<Is...>)
218  -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
219 } // namespace tuple
220 
221 #if FMTQUILL_MSC_VERSION && FMTQUILL_MSC_VERSION < 1920
222 // Older MSVC doesn't get the reference type correctly for arrays.
223 template <typename R> struct range_reference_type_impl {
224  using type = decltype(*detail::range_begin(std::declval<R&>()));
225 };
226 
227 template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
228  using type = T&;
229 };
230 
231 template <typename T>
232 using range_reference_type = typename range_reference_type_impl<T>::type;
233 #else
234 template <typename Range>
235 using range_reference_type =
236  decltype(*detail::range_begin(std::declval<Range&>()));
237 #endif
238 
239 // We don't use the Range's value_type for anything, but we do need the Range's
240 // reference type, with cv-ref stripped.
241 template <typename Range>
242 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
243 
244 template <typename T>
246  : std::integral_constant<range_format,
247  std::is_same<uncvref_type<T>, T>::value
248  ? range_format::disabled
249  : is_map<T>::value ? range_format::map
250  : is_set<T>::value ? range_format::set
251  : range_format::sequence> {};
252 
253 template <range_format K>
254 using range_format_constant = std::integral_constant<range_format, K>;
255 
256 // These are not generic lambdas for compatibility with C++11.
257 template <typename Char> struct parse_empty_specs {
258  template <typename Formatter> FMTQUILL_CONSTEXPR void operator()(Formatter& f) {
259  f.parse(ctx);
260  detail::maybe_set_debug_format(f, true);
261  }
262  parse_context<Char>& ctx;
263 };
264 template <typename FormatContext> struct format_tuple_element {
265  using char_type = typename FormatContext::char_type;
266 
267  template <typename T>
268  void operator()(const formatter<T, char_type>& f, const T& v) {
269  if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
270  ctx.advance_to(f.format(v, ctx));
271  ++i;
272  }
273 
274  int i;
275  FormatContext& ctx;
277 };
278 
279 } // namespace detail
280 
281 FMTQUILL_EXPORT
282 template <typename T> struct is_tuple_like {
283  static constexpr bool value =
285 };
286 
287 FMTQUILL_EXPORT
288 template <typename T, typename C> struct is_tuple_formattable {
289  static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
290 };
291 
292 template <typename Tuple, typename Char>
293 struct formatter<Tuple, Char,
294  enable_if_t<fmtquill::is_tuple_like<Tuple>::value &&
295  fmtquill::is_tuple_formattable<Tuple, Char>::value>> {
296  private:
297  decltype(detail::tuple::get_formatters<Tuple, Char>(
298  detail::tuple_index_sequence<Tuple>())) formatters_;
299 
301  basic_string_view<Char> opening_bracket_ =
303  basic_string_view<Char> closing_bracket_ =
305 
306  public:
307  FMTQUILL_CONSTEXPR formatter() {}
308 
309  FMTQUILL_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
310  separator_ = sep;
311  }
312 
313  FMTQUILL_CONSTEXPR void set_brackets(basic_string_view<Char> open,
314  basic_string_view<Char> close) {
315  opening_bracket_ = open;
316  closing_bracket_ = close;
317  }
318 
319  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
320  auto it = ctx.begin();
321  auto end = ctx.end();
322  if (it != end && detail::to_ascii(*it) == 'n') {
323  ++it;
324  set_brackets({}, {});
325  set_separator({});
326  }
327  if (it != end && *it != '}') report_error("invalid format specifier");
328  ctx.advance_to(it);
329  detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
330  return it;
331  }
332 
333  template <typename FormatContext>
334  auto format(const Tuple& value, FormatContext& ctx) const
335  -> decltype(ctx.out()) {
336  ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
337  detail::for_each2(
338  formatters_, value,
339  detail::format_tuple_element<FormatContext>{0, ctx, separator_});
340  return detail::copy<Char>(closing_bracket_, ctx.out());
341  }
342 };
343 
344 FMTQUILL_EXPORT
345 template <typename T, typename Char> struct is_range {
346  static constexpr bool value =
348 };
349 
350 namespace detail {
351 
352 template <typename Char, typename Element>
353 using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
354 
355 template <typename R>
356 using maybe_const_range =
357  conditional_t<has_const_begin_end<R>::value, const R, R>;
358 
359 template <typename R, typename Char>
361  : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
362 } // namespace detail
363 
364 template <typename...> struct conjunction : std::true_type {};
365 template <typename P> struct conjunction<P> : P {};
366 template <typename P1, typename... Pn>
367 struct conjunction<P1, Pn...>
368  : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
369 
370 FMTQUILL_EXPORT
371 template <typename T, typename Char, typename Enable = void>
373 
374 template <typename T, typename Char>
376  T, Char,
377  enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
378  is_formattable<T, Char>>::value>> {
379  private:
382  basic_string_view<Char> opening_bracket_ =
384  basic_string_view<Char> closing_bracket_ =
386  bool is_debug = false;
387 
388  template <typename Output, typename It, typename Sentinel, typename U = T,
389  FMTQUILL_ENABLE_IF(std::is_same<U, Char>::value)>
390  auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
391  auto buf = basic_memory_buffer<Char>();
392  for (; it != end; ++it) buf.push_back(*it);
393  auto specs = format_specs();
394  specs.set_type(presentation_type::debug);
395  return detail::write<Char>(
396  out, basic_string_view<Char>(buf.data(), buf.size()), specs);
397  }
398 
399  template <typename Output, typename It, typename Sentinel, typename U = T,
400  FMTQUILL_ENABLE_IF(!std::is_same<U, Char>::value)>
401  auto write_debug_string(Output& out, It, Sentinel) const -> Output {
402  return out;
403  }
404 
405  public:
406  FMTQUILL_CONSTEXPR range_formatter() {}
407 
408  FMTQUILL_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
409  return underlying_;
410  }
411 
412  FMTQUILL_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
413  separator_ = sep;
414  }
415 
416  FMTQUILL_CONSTEXPR void set_brackets(basic_string_view<Char> open,
417  basic_string_view<Char> close) {
418  opening_bracket_ = open;
419  closing_bracket_ = close;
420  }
421 
422  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
423  auto it = ctx.begin();
424  auto end = ctx.end();
425  detail::maybe_set_debug_format(underlying_, true);
426  if (it == end) return underlying_.parse(ctx);
427 
428  switch (detail::to_ascii(*it)) {
429  case 'n':
430  set_brackets({}, {});
431  ++it;
432  break;
433  case '?':
434  is_debug = true;
435  set_brackets({}, {});
436  ++it;
437  if (it == end || *it != 's') report_error("invalid format specifier");
438  FMTQUILL_FALLTHROUGH;
439  case 's':
440  if (!std::is_same<T, Char>::value)
441  report_error("invalid format specifier");
442  if (!is_debug) {
443  set_brackets(detail::string_literal<Char, '"'>{},
444  detail::string_literal<Char, '"'>{});
445  set_separator({});
446  detail::maybe_set_debug_format(underlying_, false);
447  }
448  ++it;
449  return it;
450  }
451 
452  if (it != end && *it != '}') {
453  if (*it != ':') report_error("invalid format specifier");
454  detail::maybe_set_debug_format(underlying_, false);
455  ++it;
456  }
457 
458  ctx.advance_to(it);
459  return underlying_.parse(ctx);
460  }
461 
462  template <typename R, typename FormatContext>
463  auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
464  auto out = ctx.out();
465  auto it = detail::range_begin(range);
466  auto end = detail::range_end(range);
467  if (is_debug) return write_debug_string(out, std::move(it), end);
468 
469  out = detail::copy<Char>(opening_bracket_, out);
470  int i = 0;
471  for (; it != end; ++it) {
472  if (i > 0) out = detail::copy<Char>(separator_, out);
473  ctx.advance_to(out);
474  auto&& item = *it; // Need an lvalue
475  out = underlying_.format(item, ctx);
476  ++i;
477  }
478  out = detail::copy<Char>(closing_bracket_, out);
479  return out;
480  }
481 };
482 
483 FMTQUILL_EXPORT
484 template <typename T, typename Char, typename Enable = void>
486  : conditional_t<
487  is_range<T, Char>::value, detail::range_format_kind_<T>,
488  std::integral_constant<range_format, range_format::disabled>> {};
489 
490 template <typename R, typename Char>
491 struct formatter<
492  R, Char,
493  enable_if_t<conjunction<
494  bool_constant<
495  range_format_kind<R, Char>::value != range_format::disabled &&
496  range_format_kind<R, Char>::value != range_format::map &&
497  range_format_kind<R, Char>::value != range_format::string &&
498  range_format_kind<R, Char>::value != range_format::debug_string>,
499  detail::is_formattable_delayed<R, Char>>::value>> {
500  private:
501  using range_type = detail::maybe_const_range<R>;
502  range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
503 
504  public:
505  using nonlocking = void;
506 
507  FMTQUILL_CONSTEXPR formatter() {
508  if (detail::const_check(range_format_kind<R, Char>::value !=
509  range_format::set))
510  return;
511  range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
513  }
514 
515  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
516  return range_formatter_.parse(ctx);
517  }
518 
519  template <typename FormatContext>
520  auto format(range_type& range, FormatContext& ctx) const
521  -> decltype(ctx.out()) {
522  return range_formatter_.format(range, ctx);
523  }
524 };
525 
526 // A map formatter.
527 template <typename R, typename Char>
528 struct formatter<
529  R, Char,
530  enable_if_t<conjunction<
531  bool_constant<range_format_kind<R, Char>::value == range_format::map>,
532  detail::is_formattable_delayed<R, Char>>::value>> {
533  private:
534  using map_type = detail::maybe_const_range<R>;
535  using element_type = detail::uncvref_type<map_type>;
536 
537  decltype(detail::tuple::get_formatters<element_type, Char>(
539  bool no_delimiters_ = false;
540 
541  public:
542  FMTQUILL_CONSTEXPR formatter() {}
543 
544  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
545  auto it = ctx.begin();
546  auto end = ctx.end();
547  if (it != end) {
548  if (detail::to_ascii(*it) == 'n') {
549  no_delimiters_ = true;
550  ++it;
551  }
552  if (it != end && *it != '}') {
553  if (*it != ':') report_error("invalid format specifier");
554  ++it;
555  }
556  ctx.advance_to(it);
557  }
558  detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
559  return it;
560  }
561 
562  template <typename FormatContext>
563  auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
564  auto out = ctx.out();
566  if (!no_delimiters_) out = detail::copy<Char>(open, out);
567  int i = 0;
569  for (auto&& value : map) {
570  if (i > 0) out = detail::copy<Char>(sep, out);
571  ctx.advance_to(out);
572  detail::for_each2(formatters_, value,
575  ++i;
576  }
578  if (!no_delimiters_) out = detail::copy<Char>(close, out);
579  return out;
580  }
581 };
582 
583 // A (debug_)string formatter.
584 template <typename R, typename Char>
585 struct formatter<
586  R, Char,
587  enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
588  range_format_kind<R, Char>::value ==
589  range_format::debug_string>> {
590  private:
591  using range_type = detail::maybe_const_range<R>;
592  using string_type =
593  conditional_t<std::is_constructible<
595  decltype(detail::range_begin(std::declval<R>())),
596  decltype(detail::range_end(std::declval<R>()))>::value,
597  detail::std_string_view<Char>, std::basic_string<Char>>;
598 
599  formatter<string_type, Char> underlying_;
600 
601  public:
602  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
603  return underlying_.parse(ctx);
604  }
605 
606  template <typename FormatContext>
607  auto format(range_type& range, FormatContext& ctx) const
608  -> decltype(ctx.out()) {
609  auto out = ctx.out();
610  if (detail::const_check(range_format_kind<R, Char>::value ==
611  range_format::debug_string))
612  *out++ = '"';
613  out = underlying_.format(
614  string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
615  if (detail::const_check(range_format_kind<R, Char>::value ==
616  range_format::debug_string))
617  *out++ = '"';
618  return out;
619  }
620 };
621 
622 template <typename It, typename Sentinel, typename Char = char>
624  It begin;
625  Sentinel end;
627 
628  join_view(It b, Sentinel e, basic_string_view<Char> s)
629  : begin(std::move(b)), end(e), sep(s) {}
630 };
631 
632 template <typename It, typename Sentinel, typename Char>
633 struct formatter<join_view<It, Sentinel, Char>, Char> {
634  private:
635  using value_type =
636 #ifdef __cpp_lib_ranges
637  std::iter_value_t<It>;
638 #else
639  typename std::iterator_traits<It>::value_type;
640 #endif
641  formatter<remove_cvref_t<value_type>, Char> value_formatter_;
642 
643  using view = conditional_t<std::is_copy_constructible<It>::value,
645  join_view<It, Sentinel, Char>>;
646 
647  public:
648  using nonlocking = void;
649 
650  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
651  return value_formatter_.parse(ctx);
652  }
653 
654  template <typename FormatContext>
655  auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
656  using iter =
657  conditional_t<std::is_copy_constructible<view>::value, It, It&>;
658  iter it = value.begin;
659  auto out = ctx.out();
660  if (it == value.end) return out;
661  out = value_formatter_.format(*it, ctx);
662  ++it;
663  while (it != value.end) {
664  out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
665  ctx.advance_to(out);
666  out = value_formatter_.format(*it, ctx);
667  ++it;
668  }
669  return out;
670  }
671 };
672 
673 FMTQUILL_EXPORT
674 template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
675  const Tuple& tuple;
677 
678  tuple_join_view(const Tuple& t, basic_string_view<Char> s)
679  : tuple(t), sep{s} {}
680 };
681 
682 // Define FMTQUILL_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
683 // support in tuple_join. It is disabled by default because of issues with
684 // the dynamic width and precision.
685 #ifndef FMTQUILL_TUPLE_JOIN_SPECIFIERS
686 # define FMTQUILL_TUPLE_JOIN_SPECIFIERS 0
687 #endif
688 
689 template <typename Tuple, typename Char>
690 struct formatter<tuple_join_view<Tuple, Char>, Char,
691  enable_if_t<is_tuple_like<Tuple>::value>> {
692  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
693  return do_parse(ctx, std::tuple_size<Tuple>());
694  }
695 
696  template <typename FormatContext>
697  auto format(const tuple_join_view<Tuple, Char>& value,
698  FormatContext& ctx) const -> typename FormatContext::iterator {
699  return do_format(value, ctx, std::tuple_size<Tuple>());
700  }
701 
702  private:
703  decltype(detail::tuple::get_formatters<Tuple, Char>(
704  detail::tuple_index_sequence<Tuple>())) formatters_;
705 
706  FMTQUILL_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
707  std::integral_constant<size_t, 0>)
708  -> const Char* {
709  return ctx.begin();
710  }
711 
712  template <size_t N>
713  FMTQUILL_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
714  std::integral_constant<size_t, N>)
715  -> const Char* {
716  auto end = ctx.begin();
717 #if FMTQUILL_TUPLE_JOIN_SPECIFIERS
718  end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
719  if (N > 1) {
720  auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
721  if (end != end1)
722  report_error("incompatible format specs for tuple elements");
723  }
724 #endif
725  return end;
726  }
727 
728  template <typename FormatContext>
729  auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
730  std::integral_constant<size_t, 0>) const ->
731  typename FormatContext::iterator {
732  return ctx.out();
733  }
734 
735  template <typename FormatContext, size_t N>
736  auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
737  std::integral_constant<size_t, N>) const ->
738  typename FormatContext::iterator {
739  using std::get;
740  auto out =
741  std::get<std::tuple_size<Tuple>::value - N>(formatters_)
742  .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
743  if (N <= 1) return out;
744  out = detail::copy<Char>(value.sep, out);
745  ctx.advance_to(out);
746  return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
747  }
748 };
749 
750 namespace detail {
751 // Check if T has an interface like a container adaptor (e.g. std::stack,
752 // std::queue, std::priority_queue).
753 template <typename T> class is_container_adaptor_like {
754  template <typename U> static auto check(U* p) -> typename U::container_type;
755  template <typename> static void check(...);
756 
757  public:
758  static constexpr bool value =
759  !std::is_void<decltype(check<T>(nullptr))>::value;
760 };
761 
762 template <typename Container> struct all {
763  const Container& c;
764  auto begin() const -> typename Container::const_iterator { return c.begin(); }
765  auto end() const -> typename Container::const_iterator { return c.end(); }
766 };
767 } // namespace detail
768 
769 template <typename T, typename Char>
770 struct formatter<
771  T, Char,
772  enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
773  bool_constant<range_format_kind<T, Char>::value ==
774  range_format::disabled>>::value>>
775  : formatter<detail::all<typename T::container_type>, Char> {
777  template <typename FormatContext>
778  auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
779  struct getter : T {
780  static auto get(const T& v) -> all {
781  return {v.*(&getter::c)}; // Access c through the derived class.
782  }
783  };
784  return formatter<all>::format(getter::get(value), ctx);
785  }
786 };
787 
788 FMTQUILL_BEGIN_EXPORT
789 
792 template <typename It, typename Sentinel>
793 auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
794  return {std::move(begin), end, sep};
795 }
796 
811 template <typename Range, FMTQUILL_ENABLE_IF(!is_tuple_like<Range>::value)>
812 auto join(Range&& r, string_view sep)
813  -> join_view<decltype(detail::range_begin(r)),
814  decltype(detail::range_end(r))> {
815  return {detail::range_begin(r), detail::range_end(r), sep};
816 }
817 
827 template <typename Tuple, FMTQUILL_ENABLE_IF(is_tuple_like<Tuple>::value)>
828 FMTQUILL_CONSTEXPR auto join(const Tuple& tuple FMTQUILL_LIFETIMEBOUND, string_view sep)
830  return {tuple, sep};
831 }
832 
842 template <typename T>
843 auto join(std::initializer_list<T> list, string_view sep)
845  return join(std::begin(list), std::end(list), sep);
846 }
847 
848 FMTQUILL_END_EXPORT
849 FMTQUILL_END_NAMESPACE
850 
851 #endif // FMTQUILL_RANGES_H_
Definition: ranges.h:360
Definition: ranges.h:288
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:809
Definition: doctest.h:539
Definition: base.h:659
Definition: base.h:850
Definition: ranges.h:44
Definition: base.h:1063
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: ranges.h:245
FMTQUILL_CONSTEXPR void advance_to(iterator it)
Advances the begin iterator to it.
Definition: base.h:886
Definition: ranges.h:97
Definition: LogFunctions.h:261
Definition: ranges.h:623
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Definition: ranges.h:148
Definition: ranges.h:485
constexpr auto end() const noexcept -> iterator
Returns an iterator past the end of the format string range being parsed.
Definition: base.h:883
Definition: ranges.h:139
Definition: ranges.h:122
Definition: ranges.h:753
Definition: base.h:2198
Definition: ranges.h:35
Definition: ranges.h:345
Definition: ranges.h:365
Definition: ranges.h:372
Definition: format.h:267
Definition: format.h:272
constexpr auto begin() const noexcept -> iterator
Returns an iterator to the beginning of the format string range being parsed.
Definition: base.h:880
Definition: ranges.h:762
Definition: ranges.h:257
Definition: ranges.h:160
Definition: ranges.h:63
Definition: ranges.h:115
Definition: ranges.h:264
Definition: base.h:977
Definition: ranges.h:95
Definition: ranges.h:674
Definition: ranges.h:364
Definition: ranges.h:282