xref: /aosp_15_r20/external/fmtlib/include/fmt/ranges.h (revision 5c90c05cd622c0a81b57953a4d343e0e489f2e08)
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 FMT_RANGES_H_
9 #define FMT_RANGES_H_
10 
11 #ifndef FMT_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 FMT_BEGIN_NAMESPACE
23 
24 FMT_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>
95 struct has_const_begin_end<
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>
103 struct has_mutable_begin_end<
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) || FMT_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 FMT_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>
152 using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
153 
154 template <typename T, typename C, bool = is_tuple_like_<T>::value>
155 class is_tuple_formattable_ {
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(
167       index_sequence<Is...>{},
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 FMT_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 FMT_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 FMT_MSC_VERSION && FMT_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 FMT_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 FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
246 
247 template <typename T>
248 struct range_format_kind_
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> FMT_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;
279   basic_string_view<char_type> separator;
280 };
281 
282 }  // namespace detail
283 
284 template <typename T> struct is_tuple_like {
285   static constexpr const bool value =
286       detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
287 };
288 
289 template <typename T, typename C> struct is_tuple_formattable {
290   static constexpr const bool value =
291       detail::is_tuple_formattable_<T, C>::value;
292 };
293 
294 template <typename Tuple, typename Char>
295 struct formatter<Tuple, Char,
296                  enable_if_t<fmt::is_tuple_like<Tuple>::value &&
297                              fmt::is_tuple_formattable<Tuple, Char>::value>> {
298  private:
299   decltype(detail::tuple::get_formatters<Tuple, Char>(
300       detail::tuple_index_sequence<Tuple>())) formatters_;
301 
302   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
303   basic_string_view<Char> opening_bracket_ =
304       detail::string_literal<Char, '('>{};
305   basic_string_view<Char> closing_bracket_ =
306       detail::string_literal<Char, ')'>{};
307 
308  public:
309   FMT_CONSTEXPR formatter() {}
310 
311   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
312     separator_ = sep;
313   }
314 
315   FMT_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   FMT_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 =
348       detail::is_range_<T>::value && !detail::has_to_string_view<T>::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 // Workaround a bug in MSVC 2015 and earlier.
361 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
362 template <typename R, typename Char>
363 struct is_formattable_delayed
364     : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
365 #endif
366 }  // namespace detail
367 
368 template <typename...> struct conjunction : std::true_type {};
369 template <typename P> struct conjunction<P> : P {};
370 template <typename P1, typename... Pn>
371 struct conjunction<P1, Pn...>
372     : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
373 
374 template <typename T, typename Char, typename Enable = void>
375 struct range_formatter;
376 
377 template <typename T, typename Char>
378 struct range_formatter<
379     T, Char,
380     enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
381                             is_formattable<T, Char>>::value>> {
382  private:
383   detail::range_formatter_type<Char, T> underlying_;
384   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
385   basic_string_view<Char> opening_bracket_ =
386       detail::string_literal<Char, '['>{};
387   basic_string_view<Char> closing_bracket_ =
388       detail::string_literal<Char, ']'>{};
389   bool is_debug = false;
390 
391   template <typename Output, typename It, typename Sentinel, typename U = T,
392             FMT_ENABLE_IF(std::is_same<U, Char>::value)>
393   auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
394     auto buf = basic_memory_buffer<Char>();
395     for (; it != end; ++it) buf.push_back(*it);
396     auto specs = format_specs();
397     specs.set_type(presentation_type::debug);
398     return detail::write<Char>(
399         out, basic_string_view<Char>(buf.data(), buf.size()), specs);
400   }
401 
402   template <typename Output, typename It, typename Sentinel, typename U = T,
403             FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
404   auto write_debug_string(Output& out, It, Sentinel) const -> Output {
405     return out;
406   }
407 
408  public:
409   FMT_CONSTEXPR range_formatter() {}
410 
411   FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
412     return underlying_;
413   }
414 
415   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
416     separator_ = sep;
417   }
418 
419   FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
420                                   basic_string_view<Char> close) {
421     opening_bracket_ = open;
422     closing_bracket_ = close;
423   }
424 
425   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
426     auto it = ctx.begin();
427     auto end = ctx.end();
428     detail::maybe_set_debug_format(underlying_, true);
429     if (it == end) return underlying_.parse(ctx);
430 
431     switch (detail::to_ascii(*it)) {
432     case 'n':
433       set_brackets({}, {});
434       ++it;
435       break;
436     case '?':
437       is_debug = true;
438       set_brackets({}, {});
439       ++it;
440       if (it == end || *it != 's') report_error("invalid format specifier");
441       FMT_FALLTHROUGH;
442     case 's':
443       if (!std::is_same<T, Char>::value)
444         report_error("invalid format specifier");
445       if (!is_debug) {
446         set_brackets(detail::string_literal<Char, '"'>{},
447                      detail::string_literal<Char, '"'>{});
448         set_separator({});
449         detail::maybe_set_debug_format(underlying_, false);
450       }
451       ++it;
452       return it;
453     }
454 
455     if (it != end && *it != '}') {
456       if (*it != ':') report_error("invalid format specifier");
457       detail::maybe_set_debug_format(underlying_, false);
458       ++it;
459     }
460 
461     ctx.advance_to(it);
462     return underlying_.parse(ctx);
463   }
464 
465   template <typename R, typename FormatContext>
466   auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
467     auto out = ctx.out();
468     auto it = detail::range_begin(range);
469     auto end = detail::range_end(range);
470     if (is_debug) return write_debug_string(out, std::move(it), end);
471 
472     out = detail::copy<Char>(opening_bracket_, out);
473     int i = 0;
474     for (; it != end; ++it) {
475       if (i > 0) out = detail::copy<Char>(separator_, out);
476       ctx.advance_to(out);
477       auto&& item = *it;  // Need an lvalue
478       out = underlying_.format(item, ctx);
479       ++i;
480     }
481     out = detail::copy<Char>(closing_bracket_, out);
482     return out;
483   }
484 };
485 
486 FMT_EXPORT
487 template <typename T, typename Char, typename Enable = void>
488 struct range_format_kind
489     : conditional_t<
490           is_range<T, Char>::value, detail::range_format_kind_<T>,
491           std::integral_constant<range_format, range_format::disabled>> {};
492 
493 template <typename R, typename Char>
494 struct formatter<
495     R, Char,
496     enable_if_t<conjunction<
497         bool_constant<
498             range_format_kind<R, Char>::value != range_format::disabled &&
499             range_format_kind<R, Char>::value != range_format::map &&
500             range_format_kind<R, Char>::value != range_format::string &&
501             range_format_kind<R, Char>::value != range_format::debug_string>
502 // Workaround a bug in MSVC 2015 and earlier.
503 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
504         ,
505         detail::is_formattable_delayed<R, Char>
506 #endif
507         >::value>> {
508  private:
509   using range_type = detail::maybe_const_range<R>;
510   range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
511 
512  public:
513   using nonlocking = void;
514 
515   FMT_CONSTEXPR formatter() {
516     if (detail::const_check(range_format_kind<R, Char>::value !=
517                             range_format::set))
518       return;
519     range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
520                                   detail::string_literal<Char, '}'>{});
521   }
522 
523   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
524     return range_formatter_.parse(ctx);
525   }
526 
527   template <typename FormatContext>
528   auto format(range_type& range, FormatContext& ctx) const
529       -> decltype(ctx.out()) {
530     return range_formatter_.format(range, ctx);
531   }
532 };
533 
534 // A map formatter.
535 template <typename R, typename Char>
536 struct formatter<
537     R, Char,
538     enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
539  private:
540   using map_type = detail::maybe_const_range<R>;
541   using element_type = detail::uncvref_type<map_type>;
542 
543   decltype(detail::tuple::get_formatters<element_type, Char>(
544       detail::tuple_index_sequence<element_type>())) formatters_;
545   bool no_delimiters_ = false;
546 
547  public:
548   FMT_CONSTEXPR formatter() {}
549 
550   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
551     auto it = ctx.begin();
552     auto end = ctx.end();
553     if (it != end) {
554       if (detail::to_ascii(*it) == 'n') {
555         no_delimiters_ = true;
556         ++it;
557       }
558       if (it != end && *it != '}') {
559         if (*it != ':') report_error("invalid format specifier");
560         ++it;
561       }
562       ctx.advance_to(it);
563     }
564     detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
565     return it;
566   }
567 
568   template <typename FormatContext>
569   auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
570     auto out = ctx.out();
571     basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
572     if (!no_delimiters_) out = detail::copy<Char>(open, out);
573     int i = 0;
574     basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
575     for (auto&& value : map) {
576       if (i > 0) out = detail::copy<Char>(sep, out);
577       ctx.advance_to(out);
578       detail::for_each2(formatters_, value,
579                         detail::format_tuple_element<FormatContext>{
580                             0, ctx, detail::string_literal<Char, ':', ' '>{}});
581       ++i;
582     }
583     basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
584     if (!no_delimiters_) out = detail::copy<Char>(close, out);
585     return out;
586   }
587 };
588 
589 // A (debug_)string formatter.
590 template <typename R, typename Char>
591 struct formatter<
592     R, Char,
593     enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
594                 range_format_kind<R, Char>::value ==
595                     range_format::debug_string>> {
596  private:
597   using range_type = detail::maybe_const_range<R>;
598   using string_type =
599       conditional_t<std::is_constructible<
600                         detail::std_string_view<Char>,
601                         decltype(detail::range_begin(std::declval<R>())),
602                         decltype(detail::range_end(std::declval<R>()))>::value,
603                     detail::std_string_view<Char>, std::basic_string<Char>>;
604 
605   formatter<string_type, Char> underlying_;
606 
607  public:
608   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
609     return underlying_.parse(ctx);
610   }
611 
612   template <typename FormatContext>
613   auto format(range_type& range, FormatContext& ctx) const
614       -> decltype(ctx.out()) {
615     auto out = ctx.out();
616     if (detail::const_check(range_format_kind<R, Char>::value ==
617                             range_format::debug_string))
618       *out++ = '"';
619     out = underlying_.format(
620         string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
621     if (detail::const_check(range_format_kind<R, Char>::value ==
622                             range_format::debug_string))
623       *out++ = '"';
624     return out;
625   }
626 };
627 
628 template <typename It, typename Sentinel, typename Char = char>
629 struct join_view : detail::view {
630   It begin;
631   Sentinel end;
632   basic_string_view<Char> sep;
633 
634   join_view(It b, Sentinel e, basic_string_view<Char> s)
635       : begin(std::move(b)), end(e), sep(s) {}
636 };
637 
638 template <typename It, typename Sentinel, typename Char>
639 struct formatter<join_view<It, Sentinel, Char>, Char> {
640  private:
641   using value_type =
642 #ifdef __cpp_lib_ranges
643       std::iter_value_t<It>;
644 #else
645       typename std::iterator_traits<It>::value_type;
646 #endif
647   formatter<remove_cvref_t<value_type>, Char> value_formatter_;
648 
649   using view_ref = conditional_t<std::is_copy_constructible<It>::value,
650                                  const join_view<It, Sentinel, Char>&,
651                                  join_view<It, Sentinel, Char>&&>;
652 
653  public:
654   using nonlocking = void;
655 
656   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
657     return value_formatter_.parse(ctx);
658   }
659 
660   template <typename FormatContext>
661   auto format(view_ref& value, FormatContext& ctx) const
662       -> decltype(ctx.out()) {
663     auto it = std::forward<view_ref>(value).begin;
664     auto out = ctx.out();
665     if (it == value.end) return out;
666     out = value_formatter_.format(*it, ctx);
667     ++it;
668     while (it != value.end) {
669       out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
670       ctx.advance_to(out);
671       out = value_formatter_.format(*it, ctx);
672       ++it;
673     }
674     return out;
675   }
676 };
677 
678 /// Returns a view that formats the iterator range `[begin, end)` with elements
679 /// separated by `sep`.
680 template <typename It, typename Sentinel>
681 auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
682   return {std::move(begin), end, sep};
683 }
684 
685 /**
686  * Returns a view that formats `range` with elements separated by `sep`.
687  *
688  * **Example**:
689  *
690  *     auto v = std::vector<int>{1, 2, 3};
691  *     fmt::print("{}", fmt::join(v, ", "));
692  *     // Output: 1, 2, 3
693  *
694  * `fmt::join` applies passed format specifiers to the range elements:
695  *
696  *     fmt::print("{:02}", fmt::join(v, ", "));
697  *     // Output: 01, 02, 03
698  */
699 template <typename Range>
700 auto join(Range&& r, string_view sep)
701     -> join_view<decltype(detail::range_begin(r)),
702                  decltype(detail::range_end(r))> {
703   return {detail::range_begin(r), detail::range_end(r), sep};
704 }
705 
706 template <typename Char, typename... T> struct tuple_join_view : detail::view {
707   const std::tuple<T...>& tuple;
708   basic_string_view<Char> sep;
709 
710   tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
711       : tuple(t), sep{s} {}
712 };
713 
714 // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
715 // support in tuple_join. It is disabled by default because of issues with
716 // the dynamic width and precision.
717 #ifndef FMT_TUPLE_JOIN_SPECIFIERS
718 #  define FMT_TUPLE_JOIN_SPECIFIERS 0
719 #endif
720 
721 template <typename Char, typename... T>
722 struct formatter<tuple_join_view<Char, T...>, Char> {
723   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
724     return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
725   }
726 
727   template <typename FormatContext>
728   auto format(const tuple_join_view<Char, T...>& value,
729               FormatContext& ctx) const -> typename FormatContext::iterator {
730     return do_format(value, ctx,
731                      std::integral_constant<size_t, sizeof...(T)>());
732   }
733 
734  private:
735   std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
736 
737   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
738                               std::integral_constant<size_t, 0>)
739       -> const Char* {
740     return ctx.begin();
741   }
742 
743   template <size_t N>
744   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
745                               std::integral_constant<size_t, N>)
746       -> const Char* {
747     auto end = ctx.begin();
748 #if FMT_TUPLE_JOIN_SPECIFIERS
749     end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
750     if (N > 1) {
751       auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
752       if (end != end1)
753         report_error("incompatible format specs for tuple elements");
754     }
755 #endif
756     return end;
757   }
758 
759   template <typename FormatContext>
760   auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
761                  std::integral_constant<size_t, 0>) const ->
762       typename FormatContext::iterator {
763     return ctx.out();
764   }
765 
766   template <typename FormatContext, size_t N>
767   auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
768                  std::integral_constant<size_t, N>) const ->
769       typename FormatContext::iterator {
770     auto out = std::get<sizeof...(T) - N>(formatters_)
771                    .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
772     if (N <= 1) return out;
773     out = detail::copy<Char>(value.sep, out);
774     ctx.advance_to(out);
775     return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
776   }
777 };
778 
779 namespace detail {
780 // Check if T has an interface like a container adaptor (e.g. std::stack,
781 // std::queue, std::priority_queue).
782 template <typename T> class is_container_adaptor_like {
783   template <typename U> static auto check(U* p) -> typename U::container_type;
784   template <typename> static void check(...);
785 
786  public:
787   static constexpr const bool value =
788       !std::is_void<decltype(check<T>(nullptr))>::value;
789 };
790 
791 template <typename Container> struct all {
792   const Container& c;
793   auto begin() const -> typename Container::const_iterator { return c.begin(); }
794   auto end() const -> typename Container::const_iterator { return c.end(); }
795 };
796 }  // namespace detail
797 
798 template <typename T, typename Char>
799 struct formatter<
800     T, Char,
801     enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
802                             bool_constant<range_format_kind<T, Char>::value ==
803                                           range_format::disabled>>::value>>
804     : formatter<detail::all<typename T::container_type>, Char> {
805   using all = detail::all<typename T::container_type>;
806   template <typename FormatContext>
807   auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
808     struct getter : T {
809       static auto get(const T& t) -> all {
810         return {t.*(&getter::c)};  // Access c through the derived class.
811       }
812     };
813     return formatter<all>::format(getter::get(t), ctx);
814   }
815 };
816 
817 FMT_BEGIN_EXPORT
818 
819 /**
820  * Returns an object that formats `std::tuple` with elements separated by `sep`.
821  *
822  * **Example**:
823  *
824  *     auto t = std::tuple<int, char>{1, 'a'};
825  *     fmt::print("{}", fmt::join(t, ", "));
826  *     // Output: 1, a
827  */
828 template <typename... T>
829 FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
830     -> tuple_join_view<char, T...> {
831   return {tuple, sep};
832 }
833 
834 /**
835  * Returns an object that formats `std::initializer_list` with elements
836  * separated by `sep`.
837  *
838  * **Example**:
839  *
840  *     fmt::print("{}", fmt::join({1, 2, 3}, ", "));
841  *     // Output: "1, 2, 3"
842  */
843 template <typename T>
844 auto join(std::initializer_list<T> list, string_view sep)
845     -> join_view<const T*, const T*> {
846   return join(std::begin(list), std::end(list), sep);
847 }
848 
849 FMT_END_EXPORT
850 FMT_END_NAMESPACE
851 
852 #endif  // FMT_RANGES_H_
853