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