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