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