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