Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:24

0001 // Formatting library for C++ - experimental range support
0002 //
0003 // Copyright (c) 2012 - present, Victor Zverovich
0004 // All rights reserved.
0005 //
0006 // For the license information refer to format.h.
0007 //
0008 // Copyright (c) 2018 - present, Remotion (Igor Schulz)
0009 // All Rights Reserved
0010 // {fmt} support for ranges, containers and types tuple interface.
0011 
0012 #ifndef FMT_RANGES_H_
0013 #define FMT_RANGES_H_
0014 
0015 #include <initializer_list>
0016 #include <tuple>
0017 #include <type_traits>
0018 
0019 #include "format.h"
0020 
0021 FMT_BEGIN_NAMESPACE
0022 
0023 namespace detail {
0024 
0025 template <typename RangeT, typename OutputIterator>
0026 OutputIterator copy(const RangeT& range, OutputIterator out) {
0027   for (auto it = range.begin(), end = range.end(); it != end; ++it)
0028     *out++ = *it;
0029   return out;
0030 }
0031 
0032 template <typename OutputIterator>
0033 OutputIterator copy(const char* str, OutputIterator out) {
0034   while (*str) *out++ = *str++;
0035   return out;
0036 }
0037 
0038 template <typename OutputIterator>
0039 OutputIterator copy(char ch, OutputIterator out) {
0040   *out++ = ch;
0041   return out;
0042 }
0043 
0044 template <typename OutputIterator>
0045 OutputIterator copy(wchar_t ch, OutputIterator out) {
0046   *out++ = ch;
0047   return out;
0048 }
0049 
0050 // Returns true if T has a std::string-like interface, like std::string_view.
0051 template <typename T> class is_std_string_like {
0052   template <typename U>
0053   static auto check(U* p)
0054       -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
0055   template <typename> static void check(...);
0056 
0057  public:
0058   static constexpr const bool value =
0059       is_string<T>::value ||
0060       std::is_convertible<T, std_string_view<char>>::value ||
0061       !std::is_void<decltype(check<T>(nullptr))>::value;
0062 };
0063 
0064 template <typename Char>
0065 struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
0066 
0067 template <typename T> class is_map {
0068   template <typename U> static auto check(U*) -> typename U::mapped_type;
0069   template <typename> static void check(...);
0070 
0071  public:
0072 #ifdef FMT_FORMAT_MAP_AS_LIST
0073   static constexpr const bool value = false;
0074 #else
0075   static constexpr const bool value =
0076       !std::is_void<decltype(check<T>(nullptr))>::value;
0077 #endif
0078 };
0079 
0080 template <typename T> class is_set {
0081   template <typename U> static auto check(U*) -> typename U::key_type;
0082   template <typename> static void check(...);
0083 
0084  public:
0085 #ifdef FMT_FORMAT_SET_AS_LIST
0086   static constexpr const bool value = false;
0087 #else
0088   static constexpr const bool value =
0089       !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
0090 #endif
0091 };
0092 
0093 template <typename... Ts> struct conditional_helper {};
0094 
0095 template <typename T, typename _ = void> struct is_range_ : std::false_type {};
0096 
0097 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
0098 
0099 #  define FMT_DECLTYPE_RETURN(val)  \
0100     ->decltype(val) { return val; } \
0101     static_assert(                  \
0102         true, "")  // This makes it so that a semicolon is required after the
0103                    // macro, which helps clang-format handle the formatting.
0104 
0105 // C array overload
0106 template <typename T, std::size_t N>
0107 auto range_begin(const T (&arr)[N]) -> const T* {
0108   return arr;
0109 }
0110 template <typename T, std::size_t N>
0111 auto range_end(const T (&arr)[N]) -> const T* {
0112   return arr + N;
0113 }
0114 
0115 template <typename T, typename Enable = void>
0116 struct has_member_fn_begin_end_t : std::false_type {};
0117 
0118 template <typename T>
0119 struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
0120                                            decltype(std::declval<T>().end())>>
0121     : std::true_type {};
0122 
0123 // Member function overload
0124 template <typename T>
0125 auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
0126 template <typename T>
0127 auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
0128 
0129 // ADL overload. Only participates in overload resolution if member functions
0130 // are not found.
0131 template <typename T>
0132 auto range_begin(T&& rng)
0133     -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
0134                    decltype(begin(static_cast<T&&>(rng)))> {
0135   return begin(static_cast<T&&>(rng));
0136 }
0137 template <typename T>
0138 auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
0139                                        decltype(end(static_cast<T&&>(rng)))> {
0140   return end(static_cast<T&&>(rng));
0141 }
0142 
0143 template <typename T, typename Enable = void>
0144 struct has_const_begin_end : std::false_type {};
0145 template <typename T, typename Enable = void>
0146 struct has_mutable_begin_end : std::false_type {};
0147 
0148 template <typename T>
0149 struct has_const_begin_end<
0150     T,
0151     void_t<
0152         decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
0153         decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
0154     : std::true_type {};
0155 
0156 template <typename T>
0157 struct has_mutable_begin_end<
0158     T, void_t<decltype(detail::range_begin(std::declval<T>())),
0159               decltype(detail::range_end(std::declval<T>())),
0160               enable_if_t<std::is_copy_constructible<T>::value>>>
0161     : std::true_type {};
0162 
0163 template <typename T>
0164 struct is_range_<T, void>
0165     : std::integral_constant<bool, (has_const_begin_end<T>::value ||
0166                                     has_mutable_begin_end<T>::value)> {};
0167 #  undef FMT_DECLTYPE_RETURN
0168 #endif
0169 
0170 // tuple_size and tuple_element check.
0171 template <typename T> class is_tuple_like_ {
0172   template <typename U>
0173   static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
0174   template <typename> static void check(...);
0175 
0176  public:
0177   static constexpr const bool value =
0178       !std::is_void<decltype(check<T>(nullptr))>::value;
0179 };
0180 
0181 // Check for integer_sequence
0182 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
0183 template <typename T, T... N>
0184 using integer_sequence = std::integer_sequence<T, N...>;
0185 template <size_t... N> using index_sequence = std::index_sequence<N...>;
0186 template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
0187 #else
0188 template <typename T, T... N> struct integer_sequence {
0189   using value_type = T;
0190 
0191   static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
0192 };
0193 
0194 template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
0195 
0196 template <typename T, size_t N, T... Ns>
0197 struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
0198 template <typename T, T... Ns>
0199 struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
0200 
0201 template <size_t N>
0202 using make_index_sequence = make_integer_sequence<size_t, N>;
0203 #endif
0204 
0205 template <typename T>
0206 using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
0207 
0208 template <typename T, typename C, bool = is_tuple_like_<T>::value>
0209 class is_tuple_formattable_ {
0210  public:
0211   static constexpr const bool value = false;
0212 };
0213 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
0214   template <std::size_t... I>
0215   static std::true_type check2(index_sequence<I...>,
0216                                integer_sequence<bool, (I == I)...>);
0217   static std::false_type check2(...);
0218   template <std::size_t... I>
0219   static decltype(check2(
0220       index_sequence<I...>{},
0221       integer_sequence<
0222           bool, (is_formattable<typename std::tuple_element<I, T>::type,
0223                                 C>::value)...>{})) check(index_sequence<I...>);
0224 
0225  public:
0226   static constexpr const bool value =
0227       decltype(check(tuple_index_sequence<T>{}))::value;
0228 };
0229 
0230 template <class Tuple, class F, size_t... Is>
0231 void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
0232   using std::get;
0233   // using free function get<I>(T) now.
0234   const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
0235   (void)_;  // blocks warnings
0236 }
0237 
0238 template <class T>
0239 FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
0240     T const&) {
0241   return {};
0242 }
0243 
0244 template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
0245   const auto indexes = get_indexes(tup);
0246   for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
0247 }
0248 
0249 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
0250 // Older MSVC doesn't get the reference type correctly for arrays.
0251 template <typename R> struct range_reference_type_impl {
0252   using type = decltype(*detail::range_begin(std::declval<R&>()));
0253 };
0254 
0255 template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
0256   using type = T&;
0257 };
0258 
0259 template <typename T>
0260 using range_reference_type = typename range_reference_type_impl<T>::type;
0261 #else
0262 template <typename Range>
0263 using range_reference_type =
0264     decltype(*detail::range_begin(std::declval<Range&>()));
0265 #endif
0266 
0267 // We don't use the Range's value_type for anything, but we do need the Range's
0268 // reference type, with cv-ref stripped.
0269 template <typename Range>
0270 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
0271 
0272 template <typename Range>
0273 using uncvref_first_type =
0274     remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
0275 
0276 template <typename Range>
0277 using uncvref_second_type = remove_cvref_t<
0278     decltype(std::declval<range_reference_type<Range>>().second)>;
0279 
0280 template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
0281   *out++ = ',';
0282   *out++ = ' ';
0283   return out;
0284 }
0285 
0286 template <typename Char, typename OutputIt>
0287 auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
0288   return write_escaped_string(out, str);
0289 }
0290 
0291 template <typename Char, typename OutputIt, typename T,
0292           FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
0293 inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
0294   auto sv = std_string_view<Char>(str);
0295   return write_range_entry<Char>(out, basic_string_view<Char>(sv));
0296 }
0297 
0298 template <typename Char, typename OutputIt, typename Arg,
0299           FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
0300 OutputIt write_range_entry(OutputIt out, const Arg v) {
0301   return write_escaped_char(out, v);
0302 }
0303 
0304 template <
0305     typename Char, typename OutputIt, typename Arg,
0306     FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
0307                   !std::is_same<Arg, Char>::value)>
0308 OutputIt write_range_entry(OutputIt out, const Arg& v) {
0309   return write<Char>(out, v);
0310 }
0311 
0312 }  // namespace detail
0313 
0314 template <typename T> struct is_tuple_like {
0315   static constexpr const bool value =
0316       detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
0317 };
0318 
0319 template <typename T, typename C> struct is_tuple_formattable {
0320   static constexpr const bool value =
0321       detail::is_tuple_formattable_<T, C>::value;
0322 };
0323 
0324 template <typename TupleT, typename Char>
0325 struct formatter<TupleT, Char,
0326                  enable_if_t<fmt::is_tuple_like<TupleT>::value &&
0327                              fmt::is_tuple_formattable<TupleT, Char>::value>> {
0328  private:
0329   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
0330   basic_string_view<Char> opening_bracket_ =
0331       detail::string_literal<Char, '('>{};
0332   basic_string_view<Char> closing_bracket_ =
0333       detail::string_literal<Char, ')'>{};
0334 
0335   // C++11 generic lambda for format().
0336   template <typename FormatContext> struct format_each {
0337     template <typename T> void operator()(const T& v) {
0338       if (i > 0) out = detail::copy_str<Char>(separator, out);
0339       out = detail::write_range_entry<Char>(out, v);
0340       ++i;
0341     }
0342     int i;
0343     typename FormatContext::iterator& out;
0344     basic_string_view<Char> separator;
0345   };
0346 
0347  public:
0348   FMT_CONSTEXPR formatter() {}
0349 
0350   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
0351     separator_ = sep;
0352   }
0353 
0354   FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
0355                                   basic_string_view<Char> close) {
0356     opening_bracket_ = open;
0357     closing_bracket_ = close;
0358   }
0359 
0360   template <typename ParseContext>
0361   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
0362     return ctx.begin();
0363   }
0364 
0365   template <typename FormatContext = format_context>
0366   auto format(const TupleT& values, FormatContext& ctx) const
0367       -> decltype(ctx.out()) {
0368     auto out = ctx.out();
0369     out = detail::copy_str<Char>(opening_bracket_, out);
0370     detail::for_each(values, format_each<FormatContext>{0, out, separator_});
0371     out = detail::copy_str<Char>(closing_bracket_, out);
0372     return out;
0373   }
0374 };
0375 
0376 template <typename T, typename Char> struct is_range {
0377   static constexpr const bool value =
0378       detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
0379       !std::is_convertible<T, std::basic_string<Char>>::value &&
0380       !std::is_convertible<T, detail::std_string_view<Char>>::value;
0381 };
0382 
0383 namespace detail {
0384 template <typename Context> struct range_mapper {
0385   using mapper = arg_mapper<Context>;
0386 
0387   template <typename T,
0388             FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
0389   static auto map(T&& value) -> T&& {
0390     return static_cast<T&&>(value);
0391   }
0392   template <typename T,
0393             FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
0394   static auto map(T&& value)
0395       -> decltype(mapper().map(static_cast<T&&>(value))) {
0396     return mapper().map(static_cast<T&&>(value));
0397   }
0398 };
0399 
0400 template <typename Char, typename Element>
0401 using range_formatter_type = conditional_t<
0402     is_formattable<Element, Char>::value,
0403     formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
0404                   std::declval<Element>()))>,
0405               Char>,
0406     fallback_formatter<Element, Char>>;
0407 
0408 template <typename R>
0409 using maybe_const_range =
0410     conditional_t<has_const_begin_end<R>::value, const R, R>;
0411 
0412 // Workaround a bug in MSVC 2015 and earlier.
0413 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
0414 template <typename R, typename Char>
0415 struct is_formattable_delayed
0416     : disjunction<
0417           is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
0418           has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
0419 #endif
0420 
0421 }  // namespace detail
0422 
0423 template <typename T, typename Char, typename Enable = void>
0424 struct range_formatter;
0425 
0426 template <typename T, typename Char>
0427 struct range_formatter<
0428     T, Char,
0429     enable_if_t<conjunction<
0430         std::is_same<T, remove_cvref_t<T>>,
0431         disjunction<is_formattable<T, Char>,
0432                     detail::has_fallback_formatter<T, Char>>>::value>> {
0433  private:
0434   detail::range_formatter_type<Char, T> underlying_;
0435   bool custom_specs_ = false;
0436   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
0437   basic_string_view<Char> opening_bracket_ =
0438       detail::string_literal<Char, '['>{};
0439   basic_string_view<Char> closing_bracket_ =
0440       detail::string_literal<Char, ']'>{};
0441 
0442   template <class U>
0443   FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
0444       -> decltype(u.set_debug_format()) {
0445     u.set_debug_format();
0446   }
0447 
0448   template <class U>
0449   FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
0450 
0451   FMT_CONSTEXPR void maybe_set_debug_format() {
0452     maybe_set_debug_format(underlying_, 0);
0453   }
0454 
0455  public:
0456   FMT_CONSTEXPR range_formatter() {}
0457 
0458   FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
0459     return underlying_;
0460   }
0461 
0462   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
0463     separator_ = sep;
0464   }
0465 
0466   FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
0467                                   basic_string_view<Char> close) {
0468     opening_bracket_ = open;
0469     closing_bracket_ = close;
0470   }
0471 
0472   template <typename ParseContext>
0473   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
0474     auto it = ctx.begin();
0475     auto end = ctx.end();
0476     if (it == end || *it == '}') {
0477       maybe_set_debug_format();
0478       return it;
0479     }
0480 
0481     if (*it == 'n') {
0482       set_brackets({}, {});
0483       ++it;
0484     }
0485 
0486     if (*it == '}') {
0487       maybe_set_debug_format();
0488       return it;
0489     }
0490 
0491     if (*it != ':')
0492       FMT_THROW(format_error("no other top-level range formatters supported"));
0493 
0494     custom_specs_ = true;
0495     ++it;
0496     ctx.advance_to(it);
0497     return underlying_.parse(ctx);
0498   }
0499 
0500   template <typename R, class FormatContext>
0501   auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
0502     detail::range_mapper<buffer_context<Char>> mapper;
0503     auto out = ctx.out();
0504     out = detail::copy_str<Char>(opening_bracket_, out);
0505     int i = 0;
0506     auto it = detail::range_begin(range);
0507     auto end = detail::range_end(range);
0508     for (; it != end; ++it) {
0509       if (i > 0) out = detail::copy_str<Char>(separator_, out);
0510       ;
0511       ctx.advance_to(out);
0512       out = underlying_.format(mapper.map(*it), ctx);
0513       ++i;
0514     }
0515     out = detail::copy_str<Char>(closing_bracket_, out);
0516     return out;
0517   }
0518 };
0519 
0520 enum class range_format { disabled, map, set, sequence, string, debug_string };
0521 
0522 namespace detail {
0523 template <typename T> struct range_format_kind_ {
0524   static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
0525                                     ? range_format::disabled
0526                                 : is_map<T>::value ? range_format::map
0527                                 : is_set<T>::value ? range_format::set
0528                                                    : range_format::sequence;
0529 };
0530 
0531 template <range_format K, typename R, typename Char, typename Enable = void>
0532 struct range_default_formatter;
0533 
0534 template <range_format K>
0535 using range_format_constant = std::integral_constant<range_format, K>;
0536 
0537 template <range_format K, typename R, typename Char>
0538 struct range_default_formatter<
0539     K, R, Char,
0540     enable_if_t<(K == range_format::sequence || K == range_format::map ||
0541                  K == range_format::set)>> {
0542   using range_type = detail::maybe_const_range<R>;
0543   range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
0544 
0545   FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
0546 
0547   FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
0548     underlying_.set_brackets(detail::string_literal<Char, '{'>{},
0549                              detail::string_literal<Char, '}'>{});
0550   }
0551 
0552   FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
0553     underlying_.set_brackets(detail::string_literal<Char, '{'>{},
0554                              detail::string_literal<Char, '}'>{});
0555     underlying_.underlying().set_brackets({}, {});
0556     underlying_.underlying().set_separator(
0557         detail::string_literal<Char, ':', ' '>{});
0558   }
0559 
0560   FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
0561 
0562   template <typename ParseContext>
0563   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
0564     return underlying_.parse(ctx);
0565   }
0566 
0567   template <typename FormatContext>
0568   auto format(range_type& range, FormatContext& ctx) const
0569       -> decltype(ctx.out()) {
0570     return underlying_.format(range, ctx);
0571   }
0572 };
0573 }  // namespace detail
0574 
0575 template <typename T, typename Char, typename Enable = void>
0576 struct range_format_kind
0577     : conditional_t<
0578           is_range<T, Char>::value, detail::range_format_kind_<T>,
0579           std::integral_constant<range_format, range_format::disabled>> {};
0580 
0581 template <typename R, typename Char>
0582 struct formatter<
0583     R, Char,
0584     enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
0585                                           range_format::disabled>
0586 // Workaround a bug in MSVC 2015 and earlier.
0587 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
0588                             ,
0589                             detail::is_formattable_delayed<R, Char>
0590 #endif
0591                             >::value>>
0592     : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
0593                                       Char> {
0594 };
0595 
0596 template <typename Char, typename... T> struct tuple_join_view : detail::view {
0597   const std::tuple<T...>& tuple;
0598   basic_string_view<Char> sep;
0599 
0600   tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
0601       : tuple(t), sep{s} {}
0602 };
0603 
0604 template <typename Char, typename... T>
0605 using tuple_arg_join = tuple_join_view<Char, T...>;
0606 
0607 // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
0608 // support in tuple_join. It is disabled by default because of issues with
0609 // the dynamic width and precision.
0610 #ifndef FMT_TUPLE_JOIN_SPECIFIERS
0611 #  define FMT_TUPLE_JOIN_SPECIFIERS 0
0612 #endif
0613 
0614 template <typename Char, typename... T>
0615 struct formatter<tuple_join_view<Char, T...>, Char> {
0616   template <typename ParseContext>
0617   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
0618     return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
0619   }
0620 
0621   template <typename FormatContext>
0622   auto format(const tuple_join_view<Char, T...>& value,
0623               FormatContext& ctx) const -> typename FormatContext::iterator {
0624     return do_format(value, ctx,
0625                      std::integral_constant<size_t, sizeof...(T)>());
0626   }
0627 
0628  private:
0629   std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
0630 
0631   template <typename ParseContext>
0632   FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
0633                               std::integral_constant<size_t, 0>)
0634       -> decltype(ctx.begin()) {
0635     return ctx.begin();
0636   }
0637 
0638   template <typename ParseContext, size_t N>
0639   FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
0640                               std::integral_constant<size_t, N>)
0641       -> decltype(ctx.begin()) {
0642     auto end = ctx.begin();
0643 #if FMT_TUPLE_JOIN_SPECIFIERS
0644     end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
0645     if (N > 1) {
0646       auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
0647       if (end != end1)
0648         FMT_THROW(format_error("incompatible format specs for tuple elements"));
0649     }
0650 #endif
0651     return end;
0652   }
0653 
0654   template <typename FormatContext>
0655   auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
0656                  std::integral_constant<size_t, 0>) const ->
0657       typename FormatContext::iterator {
0658     return ctx.out();
0659   }
0660 
0661   template <typename FormatContext, size_t N>
0662   auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
0663                  std::integral_constant<size_t, N>) const ->
0664       typename FormatContext::iterator {
0665     auto out = std::get<sizeof...(T) - N>(formatters_)
0666                    .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
0667     if (N > 1) {
0668       out = std::copy(value.sep.begin(), value.sep.end(), out);
0669       ctx.advance_to(out);
0670       return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
0671     }
0672     return out;
0673   }
0674 };
0675 
0676 FMT_MODULE_EXPORT_BEGIN
0677 
0678 /**
0679   \rst
0680   Returns an object that formats `tuple` with elements separated by `sep`.
0681 
0682   **Example**::
0683 
0684     std::tuple<int, char> t = {1, 'a'};
0685     fmt::print("{}", fmt::join(t, ", "));
0686     // Output: "1, a"
0687   \endrst
0688  */
0689 template <typename... T>
0690 FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
0691     -> tuple_join_view<char, T...> {
0692   return {tuple, sep};
0693 }
0694 
0695 template <typename... T>
0696 FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
0697                         basic_string_view<wchar_t> sep)
0698     -> tuple_join_view<wchar_t, T...> {
0699   return {tuple, sep};
0700 }
0701 
0702 /**
0703   \rst
0704   Returns an object that formats `initializer_list` with elements separated by
0705   `sep`.
0706 
0707   **Example**::
0708 
0709     fmt::print("{}", fmt::join({1, 2, 3}, ", "));
0710     // Output: "1, 2, 3"
0711   \endrst
0712  */
0713 template <typename T>
0714 auto join(std::initializer_list<T> list, string_view sep)
0715     -> join_view<const T*, const T*> {
0716   return join(std::begin(list), std::end(list), sep);
0717 }
0718 
0719 FMT_MODULE_EXPORT_END
0720 FMT_END_NAMESPACE
0721 
0722 #endif  // FMT_RANGES_H_