12# include <initializer_list>
16# include <type_traits>
25enum class range_format { disabled, map, set, sequence, string, debug_string };
30 template <
typename U>
static auto check(U*) ->
typename U::mapped_type;
31 template <
typename>
static void check(...);
34 static constexpr const bool value =
35 !std::is_void<decltype(check<T>(
nullptr))>::value;
39 template <
typename U>
static auto check(U*) ->
typename U::key_type;
40 template <
typename>
static void check(...);
43 static constexpr const bool value =
44 !std::is_void<decltype(check<T>(
nullptr))>::value && !is_map<T>::value;
49template <
typename T,
typename _ =
void>
struct is_range_ : std::false_type {};
51#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
53# define FMT_DECLTYPE_RETURN(val) \
54 ->decltype(val) { return val; } \
60template <
typename T, std::
size_t N>
61auto range_begin(
const T (&arr)[N]) ->
const T* {
64template <
typename T, std::
size_t N>
65auto range_end(
const T (&arr)[N]) ->
const T* {
69template <
typename T,
typename Enable =
void>
74 decltype(std::declval<T>().end())>>
79auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(
static_cast<T&&
>(rng).begin());
81auto range_end(T&& rng) FMT_DECLTYPE_RETURN(
static_cast<T&&
>(rng).end());
86auto range_begin(T&& rng)
87 -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
88 decltype(begin(
static_cast<T&&
>(rng)))> {
89 return begin(
static_cast<T&&
>(rng));
92auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
93 decltype(end(
static_cast<T&&
>(rng)))> {
94 return end(
static_cast<T&&
>(rng));
97template <
typename T,
typename Enable =
void>
99template <
typename T,
typename Enable =
void>
104 T, void_t<decltype(*detail::range_begin(
105 std::declval<
const remove_cvref_t<T>&>())),
106 decltype(detail::range_end(
107 std::declval<
const remove_cvref_t<T>&>()))>>
112 T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
113 decltype(detail::range_end(std::declval<T&>())),
116 int>> : std::true_type {};
120 : std::integral_constant<bool, (has_const_begin_end<T>::value ||
121 has_mutable_begin_end<T>::value)> {};
122# undef FMT_DECLTYPE_RETURN
127 template <
typename U>
128 static auto check(U* p) ->
decltype(std::tuple_size<U>::value, int());
129 template <
typename>
static void check(...);
132 static constexpr const bool value =
133 !std::is_void<decltype(check<T>(
nullptr))>::value;
137#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
138template <
typename T, T... N>
140template <
size_t... N>
using index_sequence = std::index_sequence<N...>;
141template <
size_t N>
using make_index_sequence = std::make_index_sequence<N>;
144 using value_type = T;
146 static FMT_CONSTEXPR
auto size() ->
size_t {
return sizeof...(N); }
149template <
size_t... N>
using index_sequence =
integer_sequence<size_t, N...>;
151template <
typename T,
size_t N, T... Ns>
153template <
typename T, T... Ns>
161using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
163template <typename T, typename C, bool = is_tuple_like_<T>::value>
166 static constexpr const bool value =
false;
169 template <
size_t... Is>
170 static auto all_true(index_sequence<Is...>,
172 static auto all_true(...) -> std::false_type;
174 template <
size_t... Is>
175 static auto check(index_sequence<Is...>) ->
decltype(all_true(
176 index_sequence<Is...>{},
178 (is_formattable<typename std::tuple_element<Is, T>::type,
182 static constexpr const bool value =
183 decltype(check(tuple_index_sequence<T>{}))::value;
186template <
typename Tuple,
typename F,
size_t... Is>
187FMT_CONSTEXPR
void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
190 const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
191 ignore_unused(unused);
194template <
typename Tuple,
typename F>
195FMT_CONSTEXPR
void for_each(Tuple&& t, F&& f) {
196 for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
197 std::forward<Tuple>(t), std::forward<F>(f));
200template <
typename Tuple1,
typename Tuple2,
typename F,
size_t... Is>
201void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
203 const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
204 ignore_unused(unused);
207template <
typename Tuple1,
typename Tuple2,
typename F>
208void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
209 for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
210 std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
216template <
typename Char,
typename... T>
217using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
220template <
typename Tuple,
typename Char, std::size_t... Is>
221auto get_formatters(index_sequence<Is...>)
222 -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
225#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
227template <
typename R>
struct range_reference_type_impl {
228 using type =
decltype(*detail::range_begin(std::declval<R&>()));
231template <
typename T, std::
size_t N>
struct range_reference_type_impl<T[N]> {
236using range_reference_type =
typename range_reference_type_impl<T>::type;
238template <
typename Range>
239using range_reference_type =
240 decltype(*detail::range_begin(std::declval<Range&>()));
245template <
typename Range>
246using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
248template <
typename Formatter>
249FMT_CONSTEXPR
auto maybe_set_debug_format(Formatter& f,
bool set)
250 ->
decltype(f.set_debug_format(set)) {
251 f.set_debug_format(set);
253template <
typename Formatter>
254FMT_CONSTEXPR
void maybe_set_debug_format(Formatter&, ...) {}
258 : std::integral_constant<range_format,
259 std::is_same<uncvref_type<T>, T>::value
260 ? range_format::disabled
261 : is_map<T>::value ? range_format::map
262 : is_set<T>::value ? range_format::set
263 : range_format::sequence> {};
265template <range_format K>
266using range_format_constant = std::integral_constant<range_format, K>;
270 template <
typename Formatter> FMT_CONSTEXPR
void operator()(Formatter& f) {
272 detail::maybe_set_debug_format(f,
true);
277 using char_type =
typename FormatContext::char_type;
279 template <
typename T>
281 if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
282 ctx.advance_to(f.format(v, ctx));
294 static constexpr const bool value =
299 static constexpr const bool value =
300 detail::is_tuple_formattable_<T, C>::value;
303template <
typename Tuple,
typename Char>
304struct formatter<Tuple, Char,
306 fmt::is_tuple_formattable<Tuple, Char>::value>> {
308 decltype(detail::tuple::get_formatters<Tuple, Char>(
309 detail::tuple_index_sequence<Tuple>())) formatters_;
318 FMT_CONSTEXPR formatter() {}
326 opening_bracket_ = open;
327 closing_bracket_ = close;
330 template <
typename ParseContext>
331 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
332 auto it = ctx.begin();
333 if (it != ctx.end() && *it !=
'}') report_error(
"invalid format specifier");
338 template <
typename FormatContext>
339 auto format(
const Tuple& value, FormatContext& ctx)
const
340 ->
decltype(ctx.out()) {
341 ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
345 return detail::copy<Char>(closing_bracket_, ctx.out());
349template <
typename T,
typename Char>
struct is_range {
350 static constexpr const bool value =
358 template <
typename T,
359 FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>
::value)>
360 static auto map(T&&
value) -> T&& {
361 return static_cast<T&&
>(
value);
363 template <
typename T,
364 FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>
::value)>
365 static auto map(T&&
value)
366 ->
decltype(mapper().map(
static_cast<T&&
>(
value))) {
367 return mapper().map(
static_cast<T&&
>(
value));
371template <
typename Char,
typename Element>
372using range_formatter_type =
374 .map(std::declval<Element>()))>,
378using maybe_const_range =
379 conditional_t<has_const_begin_end<R>::value,
const R, R>;
382#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
383template <
typename R,
typename Char>
385 : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
391template <
typename P1,
typename... Pn>
393 : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
395template <
typename T,
typename Char,
typename Enable =
void>
398template <
typename T,
typename Char>
399struct range_formatter<
401 enable_if_t<
conjunction<std::is_same<T, remove_cvref_t<T>>,
402 is_formattable<T, Char>>::value>> {
404 detail::range_formatter_type<Char, T> underlying_;
410 bool is_debug =
false;
412 template <
typename Output,
typename It,
typename Sentinel,
typename U = T,
413 FMT_ENABLE_IF(std::is_same<U, Char>::value)>
414 auto write_debug_string(Output& out, It it, Sentinel end)
const -> Output {
416 for (; it != end; ++it) buf.push_back(*it);
418 specs.type = presentation_type::debug;
419 return detail::write<Char>(
423 template <
typename Output,
typename It,
typename Sentinel,
typename U = T,
424 FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
425 auto write_debug_string(Output& out, It, Sentinel)
const -> Output {
430 FMT_CONSTEXPR range_formatter() {}
432 FMT_CONSTEXPR
auto underlying() -> detail::range_formatter_type<Char, T>& {
442 opening_bracket_ = open;
443 closing_bracket_ = close;
446 template <
typename ParseContext>
447 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
448 auto it = ctx.begin();
449 auto end = ctx.end();
450 detail::maybe_set_debug_format(underlying_,
true);
451 if (it == end)
return underlying_.parse(ctx);
453 switch (detail::to_ascii(*it)) {
455 set_brackets({}, {});
460 set_brackets({}, {});
462 if (it == end || *it !=
's') report_error(
"invalid format specifier");
465 if (!std::is_same<T, Char>::value)
466 report_error(
"invalid format specifier");
471 detail::maybe_set_debug_format(underlying_,
false);
477 if (it != end && *it !=
'}') {
478 if (*it !=
':') report_error(
"invalid format specifier");
479 detail::maybe_set_debug_format(underlying_,
false);
484 return underlying_.parse(ctx);
487 template <
typename R,
typename FormatContext>
488 auto format(R&& range, FormatContext& ctx)
const ->
decltype(ctx.out()) {
490 auto out = ctx.out();
491 auto it = detail::range_begin(range);
492 auto end = detail::range_end(range);
493 if (is_debug)
return write_debug_string(out, std::move(it), end);
495 out = detail::copy<Char>(opening_bracket_, out);
497 for (; it != end; ++it) {
498 if (i > 0) out = detail::copy<Char>(separator_, out);
501 out = underlying_.format(mapper.map(item), ctx);
504 out = detail::copy<Char>(closing_bracket_, out);
510template <
typename T,
typename Char,
typename Enable =
void>
513 is_range<T, Char>::value, detail::range_format_kind_<T>,
514 std::integral_constant<range_format, range_format::disabled>> {};
516template <
typename R,
typename Char>
526#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
532 using range_type = detail::maybe_const_range<R>;
536 using nonlocking = void;
538 FMT_CONSTEXPR formatter() {
546 template <
typename ParseContext>
547 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
548 return range_formatter_.parse(ctx);
551 template <
typename FormatContext>
552 auto format(range_type& range, FormatContext& ctx)
const
553 ->
decltype(ctx.out()) {
554 return range_formatter_.format(range, ctx);
559template <
typename R,
typename Char>
564 using map_type = detail::maybe_const_range<R>;
565 using element_type = detail::uncvref_type<map_type>;
567 decltype(detail::tuple::get_formatters<element_type, Char>(
568 detail::tuple_index_sequence<element_type>())) formatters_;
569 bool no_delimiters_ =
false;
572 FMT_CONSTEXPR formatter() {}
574 template <
typename ParseContext>
575 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
576 auto it = ctx.begin();
577 auto end = ctx.end();
579 if (detail::to_ascii(*it) ==
'n') {
580 no_delimiters_ =
true;
583 if (it != end && *it !=
'}') {
584 if (*it !=
':') report_error(
"invalid format specifier");
593 template <
typename FormatContext>
594 auto format(map_type& map, FormatContext& ctx)
const ->
decltype(ctx.out()) {
595 auto out = ctx.out();
597 if (!no_delimiters_) out = detail::copy<Char>(open, out);
601 for (
auto&& value : map) {
602 if (i > 0) out = detail::copy<Char>(sep, out);
604 detail::for_each2(formatters_, mapper.map(value),
606 0, ctx, detail::string_literal<Char,
':',
' '>{}});
610 if (!no_delimiters_) out = detail::copy<Char>(close, out);
616template <
typename R,
typename Char>
621 range_format::debug_string>> {
623 using range_type = detail::maybe_const_range<R>;
625 conditional_t<std::is_constructible<
627 decltype(detail::range_begin(std::declval<R>())),
628 decltype(detail::range_end(std::declval<R>()))>::value,
631 formatter<string_type, Char> underlying_;
634 template <
typename ParseContext>
635 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
636 return underlying_.parse(ctx);
639 template <
typename FormatContext>
640 auto format(range_type& range, FormatContext& ctx)
const
641 ->
decltype(ctx.out()) {
642 auto out = ctx.out();
644 range_format::debug_string))
646 out = underlying_.format(
647 string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
649 range_format::debug_string))
655template <
typename It,
typename Sentinel,
typename Char =
char>
662 : begin(std::move(b)), end(e), sep(s) {}
665template <
typename It,
typename Sentinel,
typename Char>
669#ifdef __cpp_lib_ranges
670 std::iter_value_t<It>;
672 typename std::iterator_traits<It>::value_type;
674 formatter<remove_cvref_t<value_type>, Char> value_formatter_;
676 using view_ref = conditional_t<std::is_copy_constructible<It>::value,
681 using nonlocking = void;
683 template <
typename ParseContext>
684 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
const Char* {
685 return value_formatter_.parse(ctx);
688 template <
typename FormatContext>
689 auto format(view_ref& value, FormatContext& ctx)
const
690 ->
decltype(ctx.out()) {
691 auto it = std::forward<view_ref>(value).begin;
692 auto out = ctx.out();
693 if (it == value.end)
return out;
694 out = value_formatter_.format(*it, ctx);
696 while (it != value.end) {
697 out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
699 out = value_formatter_.format(*it, ctx);
708template <
typename It,
typename Sentinel>
710 return {std::move(begin), end, sep};
727template <
typename Range>
728auto join(Range&& r, string_view sep)
729 ->
join_view<
decltype(detail::range_begin(r)),
730 decltype(detail::range_end(r))> {
731 return {detail::range_begin(r), detail::range_end(r), sep};
734template <
typename Char,
typename... T>
struct tuple_join_view :
detail::view {
735 const std::tuple<T...>& tuple;
739 : tuple(t), sep{s} {}
745#ifndef FMT_TUPLE_JOIN_SPECIFIERS
746# define FMT_TUPLE_JOIN_SPECIFIERS 0
749template <
typename Char,
typename... T>
751 template <
typename ParseContext>
752 FMT_CONSTEXPR
auto parse(ParseContext& ctx) ->
decltype(ctx.begin()) {
753 return do_parse(ctx, std::integral_constant<
size_t,
sizeof...(T)>());
756 template <
typename FormatContext>
758 FormatContext& ctx)
const ->
typename FormatContext::iterator {
759 return do_format(value, ctx,
760 std::integral_constant<
size_t,
sizeof...(T)>());
764 std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
766 template <
typename ParseContext>
767 FMT_CONSTEXPR
auto do_parse(ParseContext& ctx,
768 std::integral_constant<size_t, 0>)
769 ->
decltype(ctx.begin()) {
773 template <
typename ParseContext,
size_t N>
774 FMT_CONSTEXPR
auto do_parse(ParseContext& ctx,
775 std::integral_constant<size_t, N>)
776 ->
decltype(ctx.begin()) {
777 auto end = ctx.begin();
778#if FMT_TUPLE_JOIN_SPECIFIERS
779 end = std::get<
sizeof...(T) - N>(formatters_).parse(ctx);
781 auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
783 report_error(
"incompatible format specs for tuple elements");
789 template <
typename FormatContext>
791 std::integral_constant<size_t, 0>)
const ->
792 typename FormatContext::iterator {
796 template <
typename FormatContext,
size_t N>
798 std::integral_constant<size_t, N>)
const ->
799 typename FormatContext::iterator {
800 auto out = std::get<
sizeof...(T) - N>(formatters_)
801 .format(std::get<
sizeof...(T) - N>(value.tuple), ctx);
802 if (N <= 1)
return out;
803 out = detail::copy<Char>(value.sep, out);
805 return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
813 template <
typename U>
static auto check(U* p) ->
typename U::container_type;
814 template <
typename>
static void check(...);
817 static constexpr const bool value =
818 !std::is_void<decltype(check<T>(
nullptr))>::value;
821template <
typename Container>
struct all {
823 auto begin()
const ->
typename Container::const_iterator {
return c.begin(); }
824 auto end()
const ->
typename Container::const_iterator {
return c.end(); }
828template <
typename T,
typename Char>
831 enable_if_t<
conjunction<detail::is_container_adaptor_like<T>,
832 bool_constant<range_format_kind<T, Char>::value ==
833 range_format::disabled>>::value>>
834 : formatter<detail::all<typename T::container_type>, Char> {
836 template <
typename FormatContext>
837 auto format(
const T& t, FormatContext& ctx)
const ->
decltype(ctx.out()) {
839 static auto get(
const T& t) -> all {
840 return {t.*(&getter::c)};
843 return formatter<all>::format(getter::get(t), ctx);
858template <
typename... T>
859FMT_CONSTEXPR
auto join(
const std::tuple<T...>& tuple, string_view sep)
874auto join(std::initializer_list<T> list, string_view sep)
876 return join(std::begin(list), std::end(list), sep);