/ externals / fmt / include / fmt / ranges.h
ranges.h
  1  // Formatting library for C++ - experimental range support
  2  //
  3  // Copyright (c) 2012 - present, Victor Zverovich
  4  // All rights reserved.
  5  //
  6  // For the license information refer to format.h.
  7  //
  8  // Copyright (c) 2018 - present, Remotion (Igor Schulz)
  9  // All Rights Reserved
 10  // {fmt} support for ranges, containers and types tuple interface.
 11  
 12  #ifndef FMT_RANGES_H_
 13  #define FMT_RANGES_H_
 14  
 15  #include <initializer_list>
 16  #include <tuple>
 17  #include <type_traits>
 18  
 19  #include "format.h"
 20  
 21  FMT_BEGIN_NAMESPACE
 22  
 23  namespace detail {
 24  
 25  template <typename Range, typename OutputIt>
 26  auto copy(const Range& range, OutputIt out) -> OutputIt {
 27    for (auto it = range.begin(), end = range.end(); it != end; ++it)
 28      *out++ = *it;
 29    return out;
 30  }
 31  
 32  template <typename OutputIt>
 33  auto copy(const char* str, OutputIt out) -> OutputIt {
 34    while (*str) *out++ = *str++;
 35    return out;
 36  }
 37  
 38  template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
 39    *out++ = ch;
 40    return out;
 41  }
 42  
 43  template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
 44    *out++ = ch;
 45    return out;
 46  }
 47  
 48  // Returns true if T has a std::string-like interface, like std::string_view.
 49  template <typename T> class is_std_string_like {
 50    template <typename U>
 51    static auto check(U* p)
 52        -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
 53    template <typename> static void check(...);
 54  
 55   public:
 56    static constexpr const bool value =
 57        is_string<T>::value ||
 58        std::is_convertible<T, std_string_view<char>>::value ||
 59        !std::is_void<decltype(check<T>(nullptr))>::value;
 60  };
 61  
 62  template <typename Char>
 63  struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
 64  
 65  template <typename T> class is_map {
 66    template <typename U> static auto check(U*) -> typename U::mapped_type;
 67    template <typename> static void check(...);
 68  
 69   public:
 70  #ifdef FMT_FORMAT_MAP_AS_LIST  // DEPRECATED!
 71    static constexpr const bool value = false;
 72  #else
 73    static constexpr const bool value =
 74        !std::is_void<decltype(check<T>(nullptr))>::value;
 75  #endif
 76  };
 77  
 78  template <typename T> class is_set {
 79    template <typename U> static auto check(U*) -> typename U::key_type;
 80    template <typename> static void check(...);
 81  
 82   public:
 83  #ifdef FMT_FORMAT_SET_AS_LIST  // DEPRECATED!
 84    static constexpr const bool value = false;
 85  #else
 86    static constexpr const bool value =
 87        !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
 88  #endif
 89  };
 90  
 91  template <typename... Ts> struct conditional_helper {};
 92  
 93  template <typename T, typename _ = void> struct is_range_ : std::false_type {};
 94  
 95  #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
 96  
 97  #  define FMT_DECLTYPE_RETURN(val)  \
 98      ->decltype(val) { return val; } \
 99      static_assert(                  \
100          true, "")  // This makes it so that a semicolon is required after the
101                     // macro, which helps clang-format handle the formatting.
102  
103  // C array overload
104  template <typename T, std::size_t N>
105  auto range_begin(const T (&arr)[N]) -> const T* {
106    return arr;
107  }
108  template <typename T, std::size_t N>
109  auto range_end(const T (&arr)[N]) -> const T* {
110    return arr + N;
111  }
112  
113  template <typename T, typename Enable = void>
114  struct has_member_fn_begin_end_t : std::false_type {};
115  
116  template <typename T>
117  struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
118                                             decltype(std::declval<T>().end())>>
119      : std::true_type {};
120  
121  // Member function overload
122  template <typename T>
123  auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
124  template <typename T>
125  auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
126  
127  // ADL overload. Only participates in overload resolution if member functions
128  // are not found.
129  template <typename T>
130  auto range_begin(T&& rng)
131      -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
132                     decltype(begin(static_cast<T&&>(rng)))> {
133    return begin(static_cast<T&&>(rng));
134  }
135  template <typename T>
136  auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
137                                         decltype(end(static_cast<T&&>(rng)))> {
138    return end(static_cast<T&&>(rng));
139  }
140  
141  template <typename T, typename Enable = void>
142  struct has_const_begin_end : std::false_type {};
143  template <typename T, typename Enable = void>
144  struct has_mutable_begin_end : std::false_type {};
145  
146  template <typename T>
147  struct has_const_begin_end<
148      T,
149      void_t<
150          decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
151          decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
152      : std::true_type {};
153  
154  template <typename T>
155  struct has_mutable_begin_end<
156      T, void_t<decltype(detail::range_begin(std::declval<T>())),
157                decltype(detail::range_end(std::declval<T>())),
158                // the extra int here is because older versions of MSVC don't
159                // SFINAE properly unless there are distinct types
160                int>> : std::true_type {};
161  
162  template <typename T>
163  struct is_range_<T, void>
164      : std::integral_constant<bool, (has_const_begin_end<T>::value ||
165                                      has_mutable_begin_end<T>::value)> {};
166  #  undef FMT_DECLTYPE_RETURN
167  #endif
168  
169  // tuple_size and tuple_element check.
170  template <typename T> class is_tuple_like_ {
171    template <typename U>
172    static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
173    template <typename> static void check(...);
174  
175   public:
176    static constexpr const bool value =
177        !std::is_void<decltype(check<T>(nullptr))>::value;
178  };
179  
180  // Check for integer_sequence
181  #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
182  template <typename T, T... N>
183  using integer_sequence = std::integer_sequence<T, N...>;
184  template <size_t... N> using index_sequence = std::index_sequence<N...>;
185  template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
186  #else
187  template <typename T, T... N> struct integer_sequence {
188    using value_type = T;
189  
190    static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
191  };
192  
193  template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
194  
195  template <typename T, size_t N, T... Ns>
196  struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
197  template <typename T, T... Ns>
198  struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
199  
200  template <size_t N>
201  using make_index_sequence = make_integer_sequence<size_t, N>;
202  #endif
203  
204  template <typename T>
205  using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
206  
207  template <typename T, typename C, bool = is_tuple_like_<T>::value>
208  class is_tuple_formattable_ {
209   public:
210    static constexpr const bool value = false;
211  };
212  template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
213    template <std::size_t... Is>
214    static std::true_type check2(index_sequence<Is...>,
215                                 integer_sequence<bool, (Is == Is)...>);
216    static std::false_type check2(...);
217    template <std::size_t... Is>
218    static decltype(check2(
219        index_sequence<Is...>{},
220        integer_sequence<
221            bool, (is_formattable<typename std::tuple_element<Is, T>::type,
222                                  C>::value)...>{})) check(index_sequence<Is...>);
223  
224   public:
225    static constexpr const bool value =
226        decltype(check(tuple_index_sequence<T>{}))::value;
227  };
228  
229  template <typename Tuple, typename F, size_t... Is>
230  FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
231    using std::get;
232    // Using a free function get<Is>(Tuple) now.
233    const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
234    ignore_unused(unused);
235  }
236  
237  template <typename Tuple, typename F>
238  FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
239    for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
240             std::forward<Tuple>(t), std::forward<F>(f));
241  }
242  
243  template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
244  void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
245    using std::get;
246    const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
247    ignore_unused(unused);
248  }
249  
250  template <typename Tuple1, typename Tuple2, typename F>
251  void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
252    for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
253              std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
254              std::forward<F>(f));
255  }
256  
257  namespace tuple {
258  // Workaround a bug in MSVC 2019 (v140).
259  template <typename Char, typename... T>
260  using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
261  
262  using std::get;
263  template <typename Tuple, typename Char, std::size_t... Is>
264  auto get_formatters(index_sequence<Is...>)
265      -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
266  }  // namespace tuple
267  
268  #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
269  // Older MSVC doesn't get the reference type correctly for arrays.
270  template <typename R> struct range_reference_type_impl {
271    using type = decltype(*detail::range_begin(std::declval<R&>()));
272  };
273  
274  template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
275    using type = T&;
276  };
277  
278  template <typename T>
279  using range_reference_type = typename range_reference_type_impl<T>::type;
280  #else
281  template <typename Range>
282  using range_reference_type =
283      decltype(*detail::range_begin(std::declval<Range&>()));
284  #endif
285  
286  // We don't use the Range's value_type for anything, but we do need the Range's
287  // reference type, with cv-ref stripped.
288  template <typename Range>
289  using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
290  
291  template <typename Formatter>
292  FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
293      -> decltype(f.set_debug_format(set)) {
294    f.set_debug_format(set);
295  }
296  template <typename Formatter>
297  FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
298  
299  // These are not generic lambdas for compatibility with C++11.
300  template <typename ParseContext> struct parse_empty_specs {
301    template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
302      f.parse(ctx);
303      detail::maybe_set_debug_format(f, true);
304    }
305    ParseContext& ctx;
306  };
307  template <typename FormatContext> struct format_tuple_element {
308    using char_type = typename FormatContext::char_type;
309  
310    template <typename T>
311    void operator()(const formatter<T, char_type>& f, const T& v) {
312      if (i > 0)
313        ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
314      ctx.advance_to(f.format(v, ctx));
315      ++i;
316    }
317  
318    int i;
319    FormatContext& ctx;
320    basic_string_view<char_type> separator;
321  };
322  
323  }  // namespace detail
324  
325  template <typename T> struct is_tuple_like {
326    static constexpr const bool value =
327        detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
328  };
329  
330  template <typename T, typename C> struct is_tuple_formattable {
331    static constexpr const bool value =
332        detail::is_tuple_formattable_<T, C>::value;
333  };
334  
335  template <typename Tuple, typename Char>
336  struct formatter<Tuple, Char,
337                   enable_if_t<fmt::is_tuple_like<Tuple>::value &&
338                               fmt::is_tuple_formattable<Tuple, Char>::value>> {
339   private:
340    decltype(detail::tuple::get_formatters<Tuple, Char>(
341        detail::tuple_index_sequence<Tuple>())) formatters_;
342  
343    basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
344    basic_string_view<Char> opening_bracket_ =
345        detail::string_literal<Char, '('>{};
346    basic_string_view<Char> closing_bracket_ =
347        detail::string_literal<Char, ')'>{};
348  
349   public:
350    FMT_CONSTEXPR formatter() {}
351  
352    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
353      separator_ = sep;
354    }
355  
356    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
357                                    basic_string_view<Char> close) {
358      opening_bracket_ = open;
359      closing_bracket_ = close;
360    }
361  
362    template <typename ParseContext>
363    FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
364      auto it = ctx.begin();
365      if (it != ctx.end() && *it != '}')
366        FMT_THROW(format_error("invalid format specifier"));
367      detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
368      return it;
369    }
370  
371    template <typename FormatContext>
372    auto format(const Tuple& value, FormatContext& ctx) const
373        -> decltype(ctx.out()) {
374      ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
375      detail::for_each2(
376          formatters_, value,
377          detail::format_tuple_element<FormatContext>{0, ctx, separator_});
378      return detail::copy_str<Char>(closing_bracket_, ctx.out());
379    }
380  };
381  
382  template <typename T, typename Char> struct is_range {
383    static constexpr const bool value =
384        detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
385        !std::is_convertible<T, std::basic_string<Char>>::value &&
386        !std::is_convertible<T, detail::std_string_view<Char>>::value;
387  };
388  
389  namespace detail {
390  template <typename Context> struct range_mapper {
391    using mapper = arg_mapper<Context>;
392  
393    template <typename T,
394              FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
395    static auto map(T&& value) -> T&& {
396      return static_cast<T&&>(value);
397    }
398    template <typename T,
399              FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
400    static auto map(T&& value)
401        -> decltype(mapper().map(static_cast<T&&>(value))) {
402      return mapper().map(static_cast<T&&>(value));
403    }
404  };
405  
406  template <typename Char, typename Element>
407  using range_formatter_type =
408      formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
409                    std::declval<Element>()))>,
410                Char>;
411  
412  template <typename R>
413  using maybe_const_range =
414      conditional_t<has_const_begin_end<R>::value, const R, R>;
415  
416  // Workaround a bug in MSVC 2015 and earlier.
417  #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
418  template <typename R, typename Char>
419  struct is_formattable_delayed
420      : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
421  #endif
422  }  // namespace detail
423  
424  template <typename T, typename Char, typename Enable = void>
425  struct range_formatter;
426  
427  template <typename T, typename Char>
428  struct range_formatter<
429      T, Char,
430      enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
431                              is_formattable<T, Char>>::value>> {
432   private:
433    detail::range_formatter_type<Char, T> underlying_;
434    basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
435    basic_string_view<Char> opening_bracket_ =
436        detail::string_literal<Char, '['>{};
437    basic_string_view<Char> closing_bracket_ =
438        detail::string_literal<Char, ']'>{};
439  
440   public:
441    FMT_CONSTEXPR range_formatter() {}
442  
443    FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
444      return underlying_;
445    }
446  
447    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
448      separator_ = sep;
449    }
450  
451    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
452                                    basic_string_view<Char> close) {
453      opening_bracket_ = open;
454      closing_bracket_ = close;
455    }
456  
457    template <typename ParseContext>
458    FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
459      auto it = ctx.begin();
460      auto end = ctx.end();
461  
462      if (it != end && *it == 'n') {
463        set_brackets({}, {});
464        ++it;
465      }
466  
467      if (it != end && *it != '}') {
468        if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
469        ++it;
470      } else {
471        detail::maybe_set_debug_format(underlying_, true);
472      }
473  
474      ctx.advance_to(it);
475      return underlying_.parse(ctx);
476    }
477  
478    template <typename R, typename FormatContext>
479    auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
480      detail::range_mapper<buffer_context<Char>> mapper;
481      auto out = ctx.out();
482      out = detail::copy_str<Char>(opening_bracket_, out);
483      int i = 0;
484      auto it = detail::range_begin(range);
485      auto end = detail::range_end(range);
486      for (; it != end; ++it) {
487        if (i > 0) out = detail::copy_str<Char>(separator_, out);
488        ctx.advance_to(out);
489        out = underlying_.format(mapper.map(*it), ctx);
490        ++i;
491      }
492      out = detail::copy_str<Char>(closing_bracket_, out);
493      return out;
494    }
495  };
496  
497  enum class range_format { disabled, map, set, sequence, string, debug_string };
498  
499  namespace detail {
500  template <typename T>
501  struct range_format_kind_
502      : std::integral_constant<range_format,
503                               std::is_same<uncvref_type<T>, T>::value
504                                   ? range_format::disabled
505                               : is_map<T>::value ? range_format::map
506                               : is_set<T>::value ? range_format::set
507                                                  : range_format::sequence> {};
508  
509  template <range_format K, typename R, typename Char, typename Enable = void>
510  struct range_default_formatter;
511  
512  template <range_format K>
513  using range_format_constant = std::integral_constant<range_format, K>;
514  
515  template <range_format K, typename R, typename Char>
516  struct range_default_formatter<
517      K, R, Char,
518      enable_if_t<(K == range_format::sequence || K == range_format::map ||
519                   K == range_format::set)>> {
520    using range_type = detail::maybe_const_range<R>;
521    range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
522  
523    FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
524  
525    FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
526      underlying_.set_brackets(detail::string_literal<Char, '{'>{},
527                               detail::string_literal<Char, '}'>{});
528    }
529  
530    FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
531      underlying_.set_brackets(detail::string_literal<Char, '{'>{},
532                               detail::string_literal<Char, '}'>{});
533      underlying_.underlying().set_brackets({}, {});
534      underlying_.underlying().set_separator(
535          detail::string_literal<Char, ':', ' '>{});
536    }
537  
538    FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
539  
540    template <typename ParseContext>
541    FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
542      return underlying_.parse(ctx);
543    }
544  
545    template <typename FormatContext>
546    auto format(range_type& range, FormatContext& ctx) const
547        -> decltype(ctx.out()) {
548      return underlying_.format(range, ctx);
549    }
550  };
551  }  // namespace detail
552  
553  template <typename T, typename Char, typename Enable = void>
554  struct range_format_kind
555      : conditional_t<
556            is_range<T, Char>::value, detail::range_format_kind_<T>,
557            std::integral_constant<range_format, range_format::disabled>> {};
558  
559  template <typename R, typename Char>
560  struct formatter<
561      R, Char,
562      enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
563                                            range_format::disabled>
564  // Workaround a bug in MSVC 2015 and earlier.
565  #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
566                              ,
567                              detail::is_formattable_delayed<R, Char>
568  #endif
569                              >::value>>
570      : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
571                                        Char> {
572  };
573  
574  template <typename Char, typename... T> struct tuple_join_view : detail::view {
575    const std::tuple<T...>& tuple;
576    basic_string_view<Char> sep;
577  
578    tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
579        : tuple(t), sep{s} {}
580  };
581  
582  // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
583  // support in tuple_join. It is disabled by default because of issues with
584  // the dynamic width and precision.
585  #ifndef FMT_TUPLE_JOIN_SPECIFIERS
586  #  define FMT_TUPLE_JOIN_SPECIFIERS 0
587  #endif
588  
589  template <typename Char, typename... T>
590  struct formatter<tuple_join_view<Char, T...>, Char> {
591    template <typename ParseContext>
592    FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
593      return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
594    }
595  
596    template <typename FormatContext>
597    auto format(const tuple_join_view<Char, T...>& value,
598                FormatContext& ctx) const -> typename FormatContext::iterator {
599      return do_format(value, ctx,
600                       std::integral_constant<size_t, sizeof...(T)>());
601    }
602  
603   private:
604    std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
605  
606    template <typename ParseContext>
607    FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
608                                std::integral_constant<size_t, 0>)
609        -> decltype(ctx.begin()) {
610      return ctx.begin();
611    }
612  
613    template <typename ParseContext, size_t N>
614    FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
615                                std::integral_constant<size_t, N>)
616        -> decltype(ctx.begin()) {
617      auto end = ctx.begin();
618  #if FMT_TUPLE_JOIN_SPECIFIERS
619      end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
620      if (N > 1) {
621        auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
622        if (end != end1)
623          FMT_THROW(format_error("incompatible format specs for tuple elements"));
624      }
625  #endif
626      return end;
627    }
628  
629    template <typename FormatContext>
630    auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
631                   std::integral_constant<size_t, 0>) const ->
632        typename FormatContext::iterator {
633      return ctx.out();
634    }
635  
636    template <typename FormatContext, size_t N>
637    auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
638                   std::integral_constant<size_t, N>) const ->
639        typename FormatContext::iterator {
640      auto out = std::get<sizeof...(T) - N>(formatters_)
641                     .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
642      if (N > 1) {
643        out = std::copy(value.sep.begin(), value.sep.end(), out);
644        ctx.advance_to(out);
645        return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
646      }
647      return out;
648    }
649  };
650  
651  namespace detail {
652  // Check if T has an interface like a container adaptor (e.g. std::stack,
653  // std::queue, std::priority_queue).
654  template <typename T> class is_container_adaptor_like {
655    template <typename U> static auto check(U* p) -> typename U::container_type;
656    template <typename> static void check(...);
657  
658   public:
659    static constexpr const bool value =
660        !std::is_void<decltype(check<T>(nullptr))>::value;
661  };
662  
663  template <typename Container> struct all {
664    const Container& c;
665    auto begin() const -> typename Container::const_iterator { return c.begin(); }
666    auto end() const -> typename Container::const_iterator { return c.end(); }
667  };
668  }  // namespace detail
669  
670  template <typename T, typename Char>
671  struct formatter<
672      T, Char,
673      enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
674                              bool_constant<range_format_kind<T, Char>::value ==
675                                            range_format::disabled>>::value>>
676      : formatter<detail::all<typename T::container_type>, Char> {
677    using all = detail::all<typename T::container_type>;
678    template <typename FormatContext>
679    auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
680      struct getter : T {
681        static auto get(const T& t) -> all {
682          return {t.*(&getter::c)};  // Access c through the derived class.
683        }
684      };
685      return formatter<all>::format(getter::get(t), ctx);
686    }
687  };
688  
689  FMT_BEGIN_EXPORT
690  
691  /**
692    \rst
693    Returns an object that formats `tuple` with elements separated by `sep`.
694  
695    **Example**::
696  
697      std::tuple<int, char> t = {1, 'a'};
698      fmt::print("{}", fmt::join(t, ", "));
699      // Output: "1, a"
700    \endrst
701   */
702  template <typename... T>
703  FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
704      -> tuple_join_view<char, T...> {
705    return {tuple, sep};
706  }
707  
708  template <typename... T>
709  FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
710                          basic_string_view<wchar_t> sep)
711      -> tuple_join_view<wchar_t, T...> {
712    return {tuple, sep};
713  }
714  
715  /**
716    \rst
717    Returns an object that formats `initializer_list` with elements separated by
718    `sep`.
719  
720    **Example**::
721  
722      fmt::print("{}", fmt::join({1, 2, 3}, ", "));
723      // Output: "1, 2, 3"
724    \endrst
725   */
726  template <typename T>
727  auto join(std::initializer_list<T> list, string_view sep)
728      -> join_view<const T*, const T*> {
729    return join(std::begin(list), std::end(list), sep);
730  }
731  
732  FMT_END_EXPORT
733  FMT_END_NAMESPACE
734  
735  #endif  // FMT_RANGES_H_