Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-20 10:10:41

0001 // Formatting library for C++ - range and tuple support
0002 //
0003 // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
0004 // All rights reserved.
0005 //
0006 // For the license information refer to format.h.
0007 
0008 #ifndef FMT_RANGES_H_
0009 #define FMT_RANGES_H_
0010 
0011 #ifndef FMT_MODULE
0012 #  include <initializer_list>
0013 #  include <iterator>
0014 #  include <string>
0015 #  include <tuple>
0016 #  include <type_traits>
0017 #  include <utility>
0018 #endif
0019 
0020 #include "format.h"
0021 
0022 FMT_BEGIN_NAMESPACE
0023 
0024 FMT_EXPORT
0025 enum class range_format { disabled, map, set, sequence, string, debug_string };
0026 
0027 namespace detail {
0028 
0029 template <typename T> class is_map {
0030   template <typename U> static auto check(U*) -> typename U::mapped_type;
0031   template <typename> static void check(...);
0032 
0033  public:
0034   static constexpr const bool value =
0035       !std::is_void<decltype(check<T>(nullptr))>::value;
0036 };
0037 
0038 template <typename T> class is_set {
0039   template <typename U> static auto check(U*) -> typename U::key_type;
0040   template <typename> static void check(...);
0041 
0042  public:
0043   static constexpr const bool value =
0044       !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
0045 };
0046 
0047 // C array overload
0048 template <typename T, std::size_t N>
0049 auto range_begin(const T (&arr)[N]) -> const T* {
0050   return arr;
0051 }
0052 template <typename T, std::size_t N>
0053 auto range_end(const T (&arr)[N]) -> const T* {
0054   return arr + N;
0055 }
0056 
0057 template <typename T, typename Enable = void>
0058 struct has_member_fn_begin_end_t : std::false_type {};
0059 
0060 template <typename T>
0061 struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
0062                                            decltype(std::declval<T>().end())>>
0063     : std::true_type {};
0064 
0065 // Member function overloads.
0066 template <typename T>
0067 auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
0068   return static_cast<T&&>(rng).begin();
0069 }
0070 template <typename T>
0071 auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
0072   return static_cast<T&&>(rng).end();
0073 }
0074 
0075 // ADL overloads. Only participate in overload resolution if member functions
0076 // are not found.
0077 template <typename T>
0078 auto range_begin(T&& rng)
0079     -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
0080                    decltype(begin(static_cast<T&&>(rng)))> {
0081   return begin(static_cast<T&&>(rng));
0082 }
0083 template <typename T>
0084 auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
0085                                        decltype(end(static_cast<T&&>(rng)))> {
0086   return end(static_cast<T&&>(rng));
0087 }
0088 
0089 template <typename T, typename Enable = void>
0090 struct has_const_begin_end : std::false_type {};
0091 template <typename T, typename Enable = void>
0092 struct has_mutable_begin_end : std::false_type {};
0093 
0094 template <typename T>
0095 struct has_const_begin_end<
0096     T, void_t<decltype(*detail::range_begin(
0097                   std::declval<const remove_cvref_t<T>&>())),
0098               decltype(detail::range_end(
0099                   std::declval<const remove_cvref_t<T>&>()))>>
0100     : std::true_type {};
0101 
0102 template <typename T>
0103 struct has_mutable_begin_end<
0104     T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
0105               decltype(detail::range_end(std::declval<T&>())),
0106               // the extra int here is because older versions of MSVC don't
0107               // SFINAE properly unless there are distinct types
0108               int>> : std::true_type {};
0109 
0110 template <typename T, typename _ = void> struct is_range_ : std::false_type {};
0111 template <typename T>
0112 struct is_range_<T, void>
0113     : std::integral_constant<bool, (has_const_begin_end<T>::value ||
0114                                     has_mutable_begin_end<T>::value)> {};
0115 
0116 // tuple_size and tuple_element check.
0117 template <typename T> class is_tuple_like_ {
0118   template <typename U, typename V = typename std::remove_cv<U>::type>
0119   static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
0120   template <typename> static void check(...);
0121 
0122  public:
0123   static constexpr const bool value =
0124       !std::is_void<decltype(check<T>(nullptr))>::value;
0125 };
0126 
0127 // Check for integer_sequence
0128 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
0129 template <typename T, T... N>
0130 using integer_sequence = std::integer_sequence<T, N...>;
0131 template <size_t... N> using index_sequence = std::index_sequence<N...>;
0132 template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
0133 #else
0134 template <typename T, T... N> struct integer_sequence {
0135   using value_type = T;
0136 
0137   static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
0138 };
0139 
0140 template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
0141 
0142 template <typename T, size_t N, T... Ns>
0143 struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
0144 template <typename T, T... Ns>
0145 struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
0146 
0147 template <size_t N>
0148 using make_index_sequence = make_integer_sequence<size_t, N>;
0149 #endif
0150 
0151 template <typename T>
0152 using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
0153 
0154 template <typename T, typename C, bool = is_tuple_like_<T>::value>
0155 class is_tuple_formattable_ {
0156  public:
0157   static constexpr const bool value = false;
0158 };
0159 template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
0160   template <size_t... Is>
0161   static auto all_true(index_sequence<Is...>,
0162                        integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
0163   static auto all_true(...) -> std::false_type;
0164 
0165   template <size_t... Is>
0166   static auto check(index_sequence<Is...>) -> decltype(all_true(
0167       index_sequence<Is...>{},
0168       integer_sequence<bool,
0169                        (is_formattable<typename std::tuple_element<Is, T>::type,
0170                                        C>::value)...>{}));
0171 
0172  public:
0173   static constexpr const bool value =
0174       decltype(check(tuple_index_sequence<T>{}))::value;
0175 };
0176 
0177 template <typename Tuple, typename F, size_t... Is>
0178 FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
0179   using std::get;
0180   // Using a free function get<Is>(Tuple) now.
0181   const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
0182   ignore_unused(unused);
0183 }
0184 
0185 template <typename Tuple, typename F>
0186 FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
0187   for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
0188            std::forward<Tuple>(t), std::forward<F>(f));
0189 }
0190 
0191 template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
0192 void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
0193   using std::get;
0194   const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
0195   ignore_unused(unused);
0196 }
0197 
0198 template <typename Tuple1, typename Tuple2, typename F>
0199 void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
0200   for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
0201             std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
0202             std::forward<F>(f));
0203 }
0204 
0205 namespace tuple {
0206 // Workaround a bug in MSVC 2019 (v140).
0207 template <typename Char, typename... T>
0208 using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
0209 
0210 using std::get;
0211 template <typename Tuple, typename Char, std::size_t... Is>
0212 auto get_formatters(index_sequence<Is...>)
0213     -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
0214 }  // namespace tuple
0215 
0216 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
0217 // Older MSVC doesn't get the reference type correctly for arrays.
0218 template <typename R> struct range_reference_type_impl {
0219   using type = decltype(*detail::range_begin(std::declval<R&>()));
0220 };
0221 
0222 template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
0223   using type = T&;
0224 };
0225 
0226 template <typename T>
0227 using range_reference_type = typename range_reference_type_impl<T>::type;
0228 #else
0229 template <typename Range>
0230 using range_reference_type =
0231     decltype(*detail::range_begin(std::declval<Range&>()));
0232 #endif
0233 
0234 // We don't use the Range's value_type for anything, but we do need the Range's
0235 // reference type, with cv-ref stripped.
0236 template <typename Range>
0237 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
0238 
0239 template <typename Formatter>
0240 FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
0241     -> decltype(f.set_debug_format(set)) {
0242   f.set_debug_format(set);
0243 }
0244 template <typename Formatter>
0245 FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
0246 
0247 template <typename T>
0248 struct range_format_kind_
0249     : std::integral_constant<range_format,
0250                              std::is_same<uncvref_type<T>, T>::value
0251                                  ? range_format::disabled
0252                              : is_map<T>::value ? range_format::map
0253                              : is_set<T>::value ? range_format::set
0254                                                 : range_format::sequence> {};
0255 
0256 template <range_format K>
0257 using range_format_constant = std::integral_constant<range_format, K>;
0258 
0259 // These are not generic lambdas for compatibility with C++11.
0260 template <typename Char> struct parse_empty_specs {
0261   template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
0262     f.parse(ctx);
0263     detail::maybe_set_debug_format(f, true);
0264   }
0265   parse_context<Char>& ctx;
0266 };
0267 template <typename FormatContext> struct format_tuple_element {
0268   using char_type = typename FormatContext::char_type;
0269 
0270   template <typename T>
0271   void operator()(const formatter<T, char_type>& f, const T& v) {
0272     if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
0273     ctx.advance_to(f.format(v, ctx));
0274     ++i;
0275   }
0276 
0277   int i;
0278   FormatContext& ctx;
0279   basic_string_view<char_type> separator;
0280 };
0281 
0282 }  // namespace detail
0283 
0284 template <typename T> struct is_tuple_like {
0285   static constexpr const bool value =
0286       detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
0287 };
0288 
0289 template <typename T, typename C> struct is_tuple_formattable {
0290   static constexpr const bool value =
0291       detail::is_tuple_formattable_<T, C>::value;
0292 };
0293 
0294 template <typename Tuple, typename Char>
0295 struct formatter<Tuple, Char,
0296                  enable_if_t<fmt::is_tuple_like<Tuple>::value &&
0297                              fmt::is_tuple_formattable<Tuple, Char>::value>> {
0298  private:
0299   decltype(detail::tuple::get_formatters<Tuple, Char>(
0300       detail::tuple_index_sequence<Tuple>())) formatters_;
0301 
0302   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
0303   basic_string_view<Char> opening_bracket_ =
0304       detail::string_literal<Char, '('>{};
0305   basic_string_view<Char> closing_bracket_ =
0306       detail::string_literal<Char, ')'>{};
0307 
0308  public:
0309   FMT_CONSTEXPR formatter() {}
0310 
0311   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
0312     separator_ = sep;
0313   }
0314 
0315   FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
0316                                   basic_string_view<Char> close) {
0317     opening_bracket_ = open;
0318     closing_bracket_ = close;
0319   }
0320 
0321   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0322     auto it = ctx.begin();
0323     auto end = ctx.end();
0324     if (it != end && detail::to_ascii(*it) == 'n') {
0325       ++it;
0326       set_brackets({}, {});
0327       set_separator({});
0328     }
0329     if (it != end && *it != '}') report_error("invalid format specifier");
0330     ctx.advance_to(it);
0331     detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
0332     return it;
0333   }
0334 
0335   template <typename FormatContext>
0336   auto format(const Tuple& value, FormatContext& ctx) const
0337       -> decltype(ctx.out()) {
0338     ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
0339     detail::for_each2(
0340         formatters_, value,
0341         detail::format_tuple_element<FormatContext>{0, ctx, separator_});
0342     return detail::copy<Char>(closing_bracket_, ctx.out());
0343   }
0344 };
0345 
0346 template <typename T, typename Char> struct is_range {
0347   static constexpr const bool value =
0348       detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
0349 };
0350 
0351 namespace detail {
0352 
0353 template <typename Char, typename Element>
0354 using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
0355 
0356 template <typename R>
0357 using maybe_const_range =
0358     conditional_t<has_const_begin_end<R>::value, const R, R>;
0359 
0360 template <typename R, typename Char>
0361 struct is_formattable_delayed
0362     : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
0363 }  // namespace detail
0364 
0365 template <typename...> struct conjunction : std::true_type {};
0366 template <typename P> struct conjunction<P> : P {};
0367 template <typename P1, typename... Pn>
0368 struct conjunction<P1, Pn...>
0369     : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
0370 
0371 template <typename T, typename Char, typename Enable = void>
0372 struct range_formatter;
0373 
0374 template <typename T, typename Char>
0375 struct range_formatter<
0376     T, Char,
0377     enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
0378                             is_formattable<T, Char>>::value>> {
0379  private:
0380   detail::range_formatter_type<Char, T> underlying_;
0381   basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
0382   basic_string_view<Char> opening_bracket_ =
0383       detail::string_literal<Char, '['>{};
0384   basic_string_view<Char> closing_bracket_ =
0385       detail::string_literal<Char, ']'>{};
0386   bool is_debug = false;
0387 
0388   template <typename Output, typename It, typename Sentinel, typename U = T,
0389             FMT_ENABLE_IF(std::is_same<U, Char>::value)>
0390   auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
0391     auto buf = basic_memory_buffer<Char>();
0392     for (; it != end; ++it) buf.push_back(*it);
0393     auto specs = format_specs();
0394     specs.set_type(presentation_type::debug);
0395     return detail::write<Char>(
0396         out, basic_string_view<Char>(buf.data(), buf.size()), specs);
0397   }
0398 
0399   template <typename Output, typename It, typename Sentinel, typename U = T,
0400             FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
0401   auto write_debug_string(Output& out, It, Sentinel) const -> Output {
0402     return out;
0403   }
0404 
0405  public:
0406   FMT_CONSTEXPR range_formatter() {}
0407 
0408   FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
0409     return underlying_;
0410   }
0411 
0412   FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
0413     separator_ = sep;
0414   }
0415 
0416   FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
0417                                   basic_string_view<Char> close) {
0418     opening_bracket_ = open;
0419     closing_bracket_ = close;
0420   }
0421 
0422   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0423     auto it = ctx.begin();
0424     auto end = ctx.end();
0425     detail::maybe_set_debug_format(underlying_, true);
0426     if (it == end) return underlying_.parse(ctx);
0427 
0428     switch (detail::to_ascii(*it)) {
0429     case 'n':
0430       set_brackets({}, {});
0431       ++it;
0432       break;
0433     case '?':
0434       is_debug = true;
0435       set_brackets({}, {});
0436       ++it;
0437       if (it == end || *it != 's') report_error("invalid format specifier");
0438       FMT_FALLTHROUGH;
0439     case 's':
0440       if (!std::is_same<T, Char>::value)
0441         report_error("invalid format specifier");
0442       if (!is_debug) {
0443         set_brackets(detail::string_literal<Char, '"'>{},
0444                      detail::string_literal<Char, '"'>{});
0445         set_separator({});
0446         detail::maybe_set_debug_format(underlying_, false);
0447       }
0448       ++it;
0449       return it;
0450     }
0451 
0452     if (it != end && *it != '}') {
0453       if (*it != ':') report_error("invalid format specifier");
0454       detail::maybe_set_debug_format(underlying_, false);
0455       ++it;
0456     }
0457 
0458     ctx.advance_to(it);
0459     return underlying_.parse(ctx);
0460   }
0461 
0462   template <typename R, typename FormatContext>
0463   auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
0464     auto out = ctx.out();
0465     auto it = detail::range_begin(range);
0466     auto end = detail::range_end(range);
0467     if (is_debug) return write_debug_string(out, std::move(it), end);
0468 
0469     out = detail::copy<Char>(opening_bracket_, out);
0470     int i = 0;
0471     for (; it != end; ++it) {
0472       if (i > 0) out = detail::copy<Char>(separator_, out);
0473       ctx.advance_to(out);
0474       auto&& item = *it;  // Need an lvalue
0475       out = underlying_.format(item, ctx);
0476       ++i;
0477     }
0478     out = detail::copy<Char>(closing_bracket_, out);
0479     return out;
0480   }
0481 };
0482 
0483 FMT_EXPORT
0484 template <typename T, typename Char, typename Enable = void>
0485 struct range_format_kind
0486     : conditional_t<
0487           is_range<T, Char>::value, detail::range_format_kind_<T>,
0488           std::integral_constant<range_format, range_format::disabled>> {};
0489 
0490 template <typename R, typename Char>
0491 struct formatter<
0492     R, Char,
0493     enable_if_t<conjunction<
0494         bool_constant<
0495             range_format_kind<R, Char>::value != range_format::disabled &&
0496             range_format_kind<R, Char>::value != range_format::map &&
0497             range_format_kind<R, Char>::value != range_format::string &&
0498             range_format_kind<R, Char>::value != range_format::debug_string>,
0499         detail::is_formattable_delayed<R, Char>>::value>> {
0500  private:
0501   using range_type = detail::maybe_const_range<R>;
0502   range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
0503 
0504  public:
0505   using nonlocking = void;
0506 
0507   FMT_CONSTEXPR formatter() {
0508     if (detail::const_check(range_format_kind<R, Char>::value !=
0509                             range_format::set))
0510       return;
0511     range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
0512                                   detail::string_literal<Char, '}'>{});
0513   }
0514 
0515   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0516     return range_formatter_.parse(ctx);
0517   }
0518 
0519   template <typename FormatContext>
0520   auto format(range_type& range, FormatContext& ctx) const
0521       -> decltype(ctx.out()) {
0522     return range_formatter_.format(range, ctx);
0523   }
0524 };
0525 
0526 // A map formatter.
0527 template <typename R, typename Char>
0528 struct formatter<
0529     R, Char,
0530     enable_if_t<conjunction<
0531         bool_constant<range_format_kind<R, Char>::value == range_format::map>,
0532         detail::is_formattable_delayed<R, Char>>::value>> {
0533  private:
0534   using map_type = detail::maybe_const_range<R>;
0535   using element_type = detail::uncvref_type<map_type>;
0536 
0537   decltype(detail::tuple::get_formatters<element_type, Char>(
0538       detail::tuple_index_sequence<element_type>())) formatters_;
0539   bool no_delimiters_ = false;
0540 
0541  public:
0542   FMT_CONSTEXPR formatter() {}
0543 
0544   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0545     auto it = ctx.begin();
0546     auto end = ctx.end();
0547     if (it != end) {
0548       if (detail::to_ascii(*it) == 'n') {
0549         no_delimiters_ = true;
0550         ++it;
0551       }
0552       if (it != end && *it != '}') {
0553         if (*it != ':') report_error("invalid format specifier");
0554         ++it;
0555       }
0556       ctx.advance_to(it);
0557     }
0558     detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
0559     return it;
0560   }
0561 
0562   template <typename FormatContext>
0563   auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
0564     auto out = ctx.out();
0565     basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
0566     if (!no_delimiters_) out = detail::copy<Char>(open, out);
0567     int i = 0;
0568     basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
0569     for (auto&& value : map) {
0570       if (i > 0) out = detail::copy<Char>(sep, out);
0571       ctx.advance_to(out);
0572       detail::for_each2(formatters_, value,
0573                         detail::format_tuple_element<FormatContext>{
0574                             0, ctx, detail::string_literal<Char, ':', ' '>{}});
0575       ++i;
0576     }
0577     basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
0578     if (!no_delimiters_) out = detail::copy<Char>(close, out);
0579     return out;
0580   }
0581 };
0582 
0583 // A (debug_)string formatter.
0584 template <typename R, typename Char>
0585 struct formatter<
0586     R, Char,
0587     enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
0588                 range_format_kind<R, Char>::value ==
0589                     range_format::debug_string>> {
0590  private:
0591   using range_type = detail::maybe_const_range<R>;
0592   using string_type =
0593       conditional_t<std::is_constructible<
0594                         detail::std_string_view<Char>,
0595                         decltype(detail::range_begin(std::declval<R>())),
0596                         decltype(detail::range_end(std::declval<R>()))>::value,
0597                     detail::std_string_view<Char>, std::basic_string<Char>>;
0598 
0599   formatter<string_type, Char> underlying_;
0600 
0601  public:
0602   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0603     return underlying_.parse(ctx);
0604   }
0605 
0606   template <typename FormatContext>
0607   auto format(range_type& range, FormatContext& ctx) const
0608       -> decltype(ctx.out()) {
0609     auto out = ctx.out();
0610     if (detail::const_check(range_format_kind<R, Char>::value ==
0611                             range_format::debug_string))
0612       *out++ = '"';
0613     out = underlying_.format(
0614         string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
0615     if (detail::const_check(range_format_kind<R, Char>::value ==
0616                             range_format::debug_string))
0617       *out++ = '"';
0618     return out;
0619   }
0620 };
0621 
0622 template <typename It, typename Sentinel, typename Char = char>
0623 struct join_view : detail::view {
0624   It begin;
0625   Sentinel end;
0626   basic_string_view<Char> sep;
0627 
0628   join_view(It b, Sentinel e, basic_string_view<Char> s)
0629       : begin(std::move(b)), end(e), sep(s) {}
0630 };
0631 
0632 template <typename It, typename Sentinel, typename Char>
0633 struct formatter<join_view<It, Sentinel, Char>, Char> {
0634  private:
0635   using value_type =
0636 #ifdef __cpp_lib_ranges
0637       std::iter_value_t<It>;
0638 #else
0639       typename std::iterator_traits<It>::value_type;
0640 #endif
0641   formatter<remove_cvref_t<value_type>, Char> value_formatter_;
0642 
0643   using view = conditional_t<std::is_copy_constructible<It>::value,
0644                              const join_view<It, Sentinel, Char>,
0645                              join_view<It, Sentinel, Char>>;
0646 
0647  public:
0648   using nonlocking = void;
0649 
0650   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0651     return value_formatter_.parse(ctx);
0652   }
0653 
0654   template <typename FormatContext>
0655   auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
0656     using iter =
0657         conditional_t<std::is_copy_constructible<view>::value, It, It&>;
0658     iter it = value.begin;
0659     auto out = ctx.out();
0660     if (it == value.end) return out;
0661     out = value_formatter_.format(*it, ctx);
0662     ++it;
0663     while (it != value.end) {
0664       out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
0665       ctx.advance_to(out);
0666       out = value_formatter_.format(*it, ctx);
0667       ++it;
0668     }
0669     return out;
0670   }
0671 };
0672 
0673 template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
0674   const Tuple& tuple;
0675   basic_string_view<Char> sep;
0676 
0677   tuple_join_view(const Tuple& t, basic_string_view<Char> s)
0678       : tuple(t), sep{s} {}
0679 };
0680 
0681 // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
0682 // support in tuple_join. It is disabled by default because of issues with
0683 // the dynamic width and precision.
0684 #ifndef FMT_TUPLE_JOIN_SPECIFIERS
0685 #  define FMT_TUPLE_JOIN_SPECIFIERS 0
0686 #endif
0687 
0688 template <typename Char, typename Tuple>
0689 struct formatter<tuple_join_view<Char, Tuple>, Char,
0690                  enable_if_t<is_tuple_like<Tuple>::value>> {
0691   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
0692     return do_parse(ctx, std::tuple_size<Tuple>());
0693   }
0694 
0695   template <typename FormatContext>
0696   auto format(const tuple_join_view<Char, Tuple>& value,
0697               FormatContext& ctx) const -> typename FormatContext::iterator {
0698     return do_format(value, ctx, std::tuple_size<Tuple>());
0699   }
0700 
0701  private:
0702   decltype(detail::tuple::get_formatters<Tuple, Char>(
0703       detail::tuple_index_sequence<Tuple>())) formatters_;
0704 
0705   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
0706                               std::integral_constant<size_t, 0>)
0707       -> const Char* {
0708     return ctx.begin();
0709   }
0710 
0711   template <size_t N>
0712   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
0713                               std::integral_constant<size_t, N>)
0714       -> const Char* {
0715     auto end = ctx.begin();
0716 #if FMT_TUPLE_JOIN_SPECIFIERS
0717     end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
0718     if (N > 1) {
0719       auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
0720       if (end != end1)
0721         report_error("incompatible format specs for tuple elements");
0722     }
0723 #endif
0724     return end;
0725   }
0726 
0727   template <typename FormatContext>
0728   auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
0729                  std::integral_constant<size_t, 0>) const ->
0730       typename FormatContext::iterator {
0731     return ctx.out();
0732   }
0733 
0734   template <typename FormatContext, size_t N>
0735   auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
0736                  std::integral_constant<size_t, N>) const ->
0737       typename FormatContext::iterator {
0738     using std::get;
0739     auto out =
0740         std::get<std::tuple_size<Tuple>::value - N>(formatters_)
0741             .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
0742     if (N <= 1) return out;
0743     out = detail::copy<Char>(value.sep, out);
0744     ctx.advance_to(out);
0745     return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
0746   }
0747 };
0748 
0749 namespace detail {
0750 // Check if T has an interface like a container adaptor (e.g. std::stack,
0751 // std::queue, std::priority_queue).
0752 template <typename T> class is_container_adaptor_like {
0753   template <typename U> static auto check(U* p) -> typename U::container_type;
0754   template <typename> static void check(...);
0755 
0756  public:
0757   static constexpr const bool value =
0758       !std::is_void<decltype(check<T>(nullptr))>::value;
0759 };
0760 
0761 template <typename Container> struct all {
0762   const Container& c;
0763   auto begin() const -> typename Container::const_iterator { return c.begin(); }
0764   auto end() const -> typename Container::const_iterator { return c.end(); }
0765 };
0766 }  // namespace detail
0767 
0768 template <typename T, typename Char>
0769 struct formatter<
0770     T, Char,
0771     enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
0772                             bool_constant<range_format_kind<T, Char>::value ==
0773                                           range_format::disabled>>::value>>
0774     : formatter<detail::all<typename T::container_type>, Char> {
0775   using all = detail::all<typename T::container_type>;
0776   template <typename FormatContext>
0777   auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
0778     struct getter : T {
0779       static auto get(const T& v) -> all {
0780         return {v.*(&getter::c)};  // Access c through the derived class.
0781       }
0782     };
0783     return formatter<all>::format(getter::get(value), ctx);
0784   }
0785 };
0786 
0787 FMT_BEGIN_EXPORT
0788 
0789 /// Returns a view that formats the iterator range `[begin, end)` with elements
0790 /// separated by `sep`.
0791 template <typename It, typename Sentinel>
0792 auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
0793   return {std::move(begin), end, sep};
0794 }
0795 
0796 /**
0797  * Returns a view that formats `range` with elements separated by `sep`.
0798  *
0799  * **Example**:
0800  *
0801  *     auto v = std::vector<int>{1, 2, 3};
0802  *     fmt::print("{}", fmt::join(v, ", "));
0803  *     // Output: 1, 2, 3
0804  *
0805  * `fmt::join` applies passed format specifiers to the range elements:
0806  *
0807  *     fmt::print("{:02}", fmt::join(v, ", "));
0808  *     // Output: 01, 02, 03
0809  */
0810 template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
0811 auto join(Range&& r, string_view sep)
0812     -> join_view<decltype(detail::range_begin(r)),
0813                  decltype(detail::range_end(r))> {
0814   return {detail::range_begin(r), detail::range_end(r), sep};
0815 }
0816 
0817 /**
0818  * Returns an object that formats `std::tuple` with elements separated by `sep`.
0819  *
0820  * **Example**:
0821  *
0822  *     auto t = std::tuple<int, char>{1, 'a'};
0823  *     fmt::print("{}", fmt::join(t, ", "));
0824  *     // Output: 1, a
0825  */
0826 template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
0827 FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
0828     -> tuple_join_view<char, Tuple> {
0829   return {tuple, sep};
0830 }
0831 
0832 /**
0833  * Returns an object that formats `std::initializer_list` with elements
0834  * separated by `sep`.
0835  *
0836  * **Example**:
0837  *
0838  *     fmt::print("{}", fmt::join({1, 2, 3}, ", "));
0839  *     // Output: "1, 2, 3"
0840  */
0841 template <typename T>
0842 auto join(std::initializer_list<T> list, string_view sep)
0843     -> join_view<const T*, const T*> {
0844   return join(std::begin(list), std::end(list), sep);
0845 }
0846 
0847 FMT_END_EXPORT
0848 FMT_END_NAMESPACE
0849 
0850 #endif  // FMT_RANGES_H_