File indexing completed on 2025-12-20 10:10:41
0001
0002
0003
0004
0005
0006
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
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
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
0076
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
0107
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
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
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
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
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 }
0215
0216 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
0217
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
0235
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
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 }
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 }
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;
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
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
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
0682
0683
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
0751
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 }
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)};
0781 }
0782 };
0783 return formatter<all>::format(getter::get(value), ctx);
0784 }
0785 };
0786
0787 FMT_BEGIN_EXPORT
0788
0789
0790
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
0798
0799
0800
0801
0802
0803
0804
0805
0806
0807
0808
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
0819
0820
0821
0822
0823
0824
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
0834
0835
0836
0837
0838
0839
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