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 <string>
15 # include <tuple>
16 # include <type_traits>
17 # include <utility>
18 #endif
19 
20 #include "format.h"
21 
22 FMTQUILL_BEGIN_NAMESPACE
23 
24 FMTQUILL_EXPORT
25 enum class range_format { disabled, map, set, sequence, string, debug_string };
26 
27 namespace detail {
28 
29 template <typename T> class is_map {
30  template <typename U> static auto check(U*) -> typename U::mapped_type;
31  template <typename> static void check(...);
32 
33  public:
34  static constexpr const bool value =
35  !std::is_void<decltype(check<T>(nullptr))>::value;
36 };
37 
38 template <typename T> class is_set {
39  template <typename U> static auto check(U*) -> typename U::key_type;
40  template <typename> static void check(...);
41 
42  public:
43  static constexpr const bool value =
44  !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
45 };
46 
47 // C array overload
48 template <typename T, std::size_t N>
49 auto range_begin(const T (&arr)[N]) -> const T* {
50  return arr;
51 }
52 template <typename T, std::size_t N>
53 auto range_end(const T (&arr)[N]) -> const T* {
54  return arr + N;
55 }
56 
57 template <typename T, typename Enable = void>
58 struct has_member_fn_begin_end_t : std::false_type {};
59 
60 template <typename T>
61 struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
62  decltype(std::declval<T>().end())>>
63  : std::true_type {};
64 
65 // Member function overloads.
66 template <typename T>
67 auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
68  return static_cast<T&&>(rng).begin();
69 }
70 template <typename T>
71 auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
72  return static_cast<T&&>(rng).end();
73 }
74 
75 // ADL overloads. Only participate in overload resolution if member functions
76 // are not found.
77 template <typename T>
78 auto range_begin(T&& rng)
79  -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
80  decltype(begin(static_cast<T&&>(rng)))> {
81  return begin(static_cast<T&&>(rng));
82 }
83 template <typename T>
84 auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
85  decltype(end(static_cast<T&&>(rng)))> {
86  return end(static_cast<T&&>(rng));
87 }
88 
89 template <typename T, typename Enable = void>
90 struct has_const_begin_end : std::false_type {};
91 template <typename T, typename Enable = void>
92 struct has_mutable_begin_end : std::false_type {};
93 
94 template <typename T>
96  T, void_t<decltype(*detail::range_begin(
97  std::declval<const remove_cvref_t<T>&>())),
98  decltype(detail::range_end(
99  std::declval<const remove_cvref_t<T>&>()))>>
100  : std::true_type {};
101 
102 template <typename T>
104  T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
105  decltype(detail::range_end(std::declval<T&>())),
106  // the extra int here is because older versions of MSVC don't
107  // SFINAE properly unless there are distinct types
108  int>> : std::true_type {};
109 
110 template <typename T, typename _ = void> struct is_range_ : std::false_type {};
111 template <typename T>
112 struct is_range_<T, void>
113  : std::integral_constant<bool, (has_const_begin_end<T>::value ||
114  has_mutable_begin_end<T>::value)> {};
115 
116 // tuple_size and tuple_element check.
117 template <typename T> class is_tuple_like_ {
118  template <typename U, typename V = typename std::remove_cv<U>::type>
119  static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
120  template <typename> static void check(...);
121 
122  public:
123  static constexpr const bool value =
124  !std::is_void<decltype(check<T>(nullptr))>::value;
125 };
126 
127 // Check for integer_sequence
128 #if defined(__cpp_lib_integer_sequence) || FMTQUILL_MSC_VERSION >= 1900
129 template <typename T, T... N>
130 using integer_sequence = std::integer_sequence<T, N...>;
131 template <size_t... N> using index_sequence = std::index_sequence<N...>;
132 template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
133 #else
134 template <typename T, T... N> struct integer_sequence {
135  using value_type = T;
136 
137  static FMTQUILL_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
138 };
139 
140 template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
141 
142 template <typename T, size_t N, T... Ns>
143 struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
144 template <typename T, T... Ns>
145 struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
146 
147 template <size_t N>
148 using make_index_sequence = make_integer_sequence<size_t, N>;
149 #endif
150 
151 template <typename T>
153 
154 template <typename T, typename C, bool = is_tuple_like_<T>::value>
156  public:
157  static constexpr const bool value = false;
158 };
159 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
160  template <size_t... Is>
161  static auto all_true(index_sequence<Is...>,
162  integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
163  static auto all_true(...) -> std::false_type;
164 
165  template <size_t... Is>
166  static auto check(index_sequence<Is...>) -> decltype(all_true(
168  integer_sequence<bool,
169  (is_formattable<typename std::tuple_element<Is, T>::type,
170  C>::value)...>{}));
171 
172  public:
173  static constexpr const bool value =
174  decltype(check(tuple_index_sequence<T>{}))::value;
175 };
176 
177 template <typename Tuple, typename F, size_t... Is>
178 FMTQUILL_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
179  using std::get;
180  // Using a free function get<Is>(Tuple) now.
181  const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
182  ignore_unused(unused);
183 }
184 
185 template <typename Tuple, typename F>
186 FMTQUILL_CONSTEXPR void for_each(Tuple&& t, F&& f) {
187  for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
188  std::forward<Tuple>(t), std::forward<F>(f));
189 }
190 
191 template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
192 void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
193  using std::get;
194  const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
195  ignore_unused(unused);
196 }
197 
198 template <typename Tuple1, typename Tuple2, typename F>
199 void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
200  for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
201  std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
202  std::forward<F>(f));
203 }
204 
205 namespace tuple {
206 // Workaround a bug in MSVC 2019 (v140).
207 template <typename Char, typename... T>
208 using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
209 
210 using std::get;
211 template <typename Tuple, typename Char, std::size_t... Is>
212 auto get_formatters(index_sequence<Is...>)
213  -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
214 } // namespace tuple
215 
216 #if FMTQUILL_MSC_VERSION && FMTQUILL_MSC_VERSION < 1920
217 // Older MSVC doesn't get the reference type correctly for arrays.
218 template <typename R> struct range_reference_type_impl {
219  using type = decltype(*detail::range_begin(std::declval<R&>()));
220 };
221 
222 template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
223  using type = T&;
224 };
225 
226 template <typename T>
227 using range_reference_type = typename range_reference_type_impl<T>::type;
228 #else
229 template <typename Range>
230 using range_reference_type =
231  decltype(*detail::range_begin(std::declval<Range&>()));
232 #endif
233 
234 // We don't use the Range's value_type for anything, but we do need the Range's
235 // reference type, with cv-ref stripped.
236 template <typename Range>
237 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
238 
239 template <typename Formatter>
240 FMTQUILL_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
241  -> decltype(f.set_debug_format(set)) {
242  f.set_debug_format(set);
243 }
244 template <typename Formatter>
245 FMTQUILL_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
246 
247 template <typename T>
249  : std::integral_constant<range_format,
250  std::is_same<uncvref_type<T>, T>::value
251  ? range_format::disabled
252  : is_map<T>::value ? range_format::map
253  : is_set<T>::value ? range_format::set
254  : range_format::sequence> {};
255 
256 template <range_format K>
257 using range_format_constant = std::integral_constant<range_format, K>;
258 
259 // These are not generic lambdas for compatibility with C++11.
260 template <typename Char> struct parse_empty_specs {
261  template <typename Formatter> FMTQUILL_CONSTEXPR void operator()(Formatter& f) {
262  f.parse(ctx);
263  detail::maybe_set_debug_format(f, true);
264  }
265  parse_context<Char>& ctx;
266 };
267 template <typename FormatContext> struct format_tuple_element {
268  using char_type = typename FormatContext::char_type;
269 
270  template <typename T>
271  void operator()(const formatter<T, char_type>& f, const T& v) {
272  if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
273  ctx.advance_to(f.format(v, ctx));
274  ++i;
275  }
276 
277  int i;
278  FormatContext& ctx;
280 };
281 
282 } // namespace detail
283 
284 template <typename T> struct is_tuple_like {
285  static constexpr const bool value =
287 };
288 
289 template <typename T, typename C> struct is_tuple_formattable {
290  static constexpr const bool value =
292 };
293 
294 template <typename Tuple, typename Char>
295 struct formatter<Tuple, Char,
296  enable_if_t<fmtquill::is_tuple_like<Tuple>::value &&
297  fmtquill::is_tuple_formattable<Tuple, Char>::value>> {
298  private:
299  decltype(detail::tuple::get_formatters<Tuple, Char>(
300  detail::tuple_index_sequence<Tuple>())) formatters_;
301 
303  basic_string_view<Char> opening_bracket_ =
305  basic_string_view<Char> closing_bracket_ =
307 
308  public:
309  FMTQUILL_CONSTEXPR formatter() {}
310 
311  FMTQUILL_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
312  separator_ = sep;
313  }
314 
315  FMTQUILL_CONSTEXPR void set_brackets(basic_string_view<Char> open,
316  basic_string_view<Char> close) {
317  opening_bracket_ = open;
318  closing_bracket_ = close;
319  }
320 
321  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
322  auto it = ctx.begin();
323  auto end = ctx.end();
324  if (it != end && detail::to_ascii(*it) == 'n') {
325  ++it;
326  set_brackets({}, {});
327  set_separator({});
328  }
329  if (it != end && *it != '}') report_error("invalid format specifier");
330  ctx.advance_to(it);
331  detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
332  return it;
333  }
334 
335  template <typename FormatContext>
336  auto format(const Tuple& value, FormatContext& ctx) const
337  -> decltype(ctx.out()) {
338  ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
339  detail::for_each2(
340  formatters_, value,
341  detail::format_tuple_element<FormatContext>{0, ctx, separator_});
342  return detail::copy<Char>(closing_bracket_, ctx.out());
343  }
344 };
345 
346 template <typename T, typename Char> struct is_range {
347  static constexpr const bool value =
349 };
350 
351 namespace detail {
352 
353 template <typename Char, typename Element>
354 using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
355 
356 template <typename R>
357 using maybe_const_range =
358  conditional_t<has_const_begin_end<R>::value, const R, R>;
359 
360 template <typename R, typename Char>
362  : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
363 } // namespace detail
364 
365 template <typename...> struct conjunction : std::true_type {};
366 template <typename P> struct conjunction<P> : P {};
367 template <typename P1, typename... Pn>
368 struct conjunction<P1, Pn...>
369  : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
370 
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 template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
674  const Tuple& tuple;
676 
677  tuple_join_view(const Tuple& t, basic_string_view<Char> s)
678  : tuple(t), sep{s} {}
679 };
680 
681 // Define FMTQUILL_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
682 // support in tuple_join. It is disabled by default because of issues with
683 // the dynamic width and precision.
684 #ifndef FMTQUILL_TUPLE_JOIN_SPECIFIERS
685 # define FMTQUILL_TUPLE_JOIN_SPECIFIERS 0
686 #endif
687 
688 template <typename Char, typename Tuple>
689 struct formatter<tuple_join_view<Char, Tuple>, Char,
690  enable_if_t<is_tuple_like<Tuple>::value>> {
691  FMTQUILL_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
692  return do_parse(ctx, std::tuple_size<Tuple>());
693  }
694 
695  template <typename FormatContext>
696  auto format(const tuple_join_view<Char, Tuple>& value,
697  FormatContext& ctx) const -> typename FormatContext::iterator {
698  return do_format(value, ctx, std::tuple_size<Tuple>());
699  }
700 
701  private:
702  decltype(detail::tuple::get_formatters<Tuple, Char>(
703  detail::tuple_index_sequence<Tuple>())) formatters_;
704 
705  FMTQUILL_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
706  std::integral_constant<size_t, 0>)
707  -> const Char* {
708  return ctx.begin();
709  }
710 
711  template <size_t N>
712  FMTQUILL_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
713  std::integral_constant<size_t, N>)
714  -> const Char* {
715  auto end = ctx.begin();
716 #if FMTQUILL_TUPLE_JOIN_SPECIFIERS
717  end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
718  if (N > 1) {
719  auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
720  if (end != end1)
721  report_error("incompatible format specs for tuple elements");
722  }
723 #endif
724  return end;
725  }
726 
727  template <typename FormatContext>
728  auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
729  std::integral_constant<size_t, 0>) const ->
730  typename FormatContext::iterator {
731  return ctx.out();
732  }
733 
734  template <typename FormatContext, size_t N>
735  auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
736  std::integral_constant<size_t, N>) const ->
737  typename FormatContext::iterator {
738  using std::get;
739  auto out =
740  std::get<std::tuple_size<Tuple>::value - N>(formatters_)
741  .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
742  if (N <= 1) return out;
743  out = detail::copy<Char>(value.sep, out);
744  ctx.advance_to(out);
745  return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
746  }
747 };
748 
749 namespace detail {
750 // Check if T has an interface like a container adaptor (e.g. std::stack,
751 // std::queue, std::priority_queue).
752 template <typename T> class is_container_adaptor_like {
753  template <typename U> static auto check(U* p) -> typename U::container_type;
754  template <typename> static void check(...);
755 
756  public:
757  static constexpr const bool value =
758  !std::is_void<decltype(check<T>(nullptr))>::value;
759 };
760 
761 template <typename Container> struct all {
762  const Container& c;
763  auto begin() const -> typename Container::const_iterator { return c.begin(); }
764  auto end() const -> typename Container::const_iterator { return c.end(); }
765 };
766 } // namespace detail
767 
768 template <typename T, typename Char>
769 struct formatter<
770  T, Char,
771  enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
772  bool_constant<range_format_kind<T, Char>::value ==
773  range_format::disabled>>::value>>
774  : formatter<detail::all<typename T::container_type>, Char> {
776  template <typename FormatContext>
777  auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
778  struct getter : T {
779  static auto get(const T& v) -> all {
780  return {v.*(&getter::c)}; // Access c through the derived class.
781  }
782  };
783  return formatter<all>::format(getter::get(value), ctx);
784  }
785 };
786 
787 FMTQUILL_BEGIN_EXPORT
788 
791 template <typename It, typename Sentinel>
792 auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
793  return {std::move(begin), end, sep};
794 }
795 
810 template <typename Range, FMTQUILL_ENABLE_IF(!is_tuple_like<Range>::value)>
811 auto join(Range&& r, string_view sep)
812  -> join_view<decltype(detail::range_begin(r)),
813  decltype(detail::range_end(r))> {
814  return {detail::range_begin(r), detail::range_end(r), sep};
815 }
816 
826 template <typename Tuple, FMTQUILL_ENABLE_IF(is_tuple_like<Tuple>::value)>
827 FMTQUILL_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
829  return {tuple, sep};
830 }
831 
841 template <typename T>
842 auto join(std::initializer_list<T> list, string_view sep)
844  return join(std::begin(list), std::end(list), sep);
845 }
846 
847 FMTQUILL_END_EXPORT
848 FMTQUILL_END_NAMESPACE
849 
850 #endif // FMTQUILL_RANGES_H_
Definition: ranges.h:361
Definition: ranges.h:289
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:790
Definition: doctest.h:539
Definition: base.h:669
Definition: base.h:860
Definition: ranges.h:38
Definition: base.h:1037
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:644
Definition: format.h:126
Definition: ranges.h:248
FMTQUILL_CONSTEXPR void advance_to(iterator it)
Advances the begin iterator to it.
Definition: base.h:896
Definition: ranges.h:92
Definition: ranges.h:623
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Definition: ranges.h:143
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:893
Definition: ranges.h:134
Definition: ranges.h:117
Definition: ranges.h:752
Definition: base.h:2147
Definition: ranges.h:29
Definition: ranges.h:346
Definition: ranges.h:366
Definition: ranges.h:372
Definition: format.h:236
Definition: format.h:241
constexpr auto begin() const noexcept -> iterator
Returns an iterator to the beginning of the format string range being parsed.
Definition: base.h:890
Definition: ranges.h:761
Definition: ranges.h:260
Definition: ranges.h:155
Definition: ranges.h:58
Definition: ranges.h:110
Definition: ranges.h:267
Definition: base.h:951
Definition: ranges.h:90
Definition: ranges.h:673
Definition: ranges.h:365
Definition: ranges.h:284