12 # include <initializer_list> 15 # include <type_traits> 24 enum class range_format { disabled, map,
set, sequence, string, debug_string };
28 template <
typename T>
class is_map {
29 template <
typename U>
static auto check(U*) ->
typename U::mapped_type;
30 template <
typename>
static void check(...);
33 static constexpr
bool value =
34 !std::is_void<decltype(check<T>(
nullptr))>::value;
37 template <
typename T>
class is_set {
38 template <
typename U>
static auto check(U*) ->
typename U::key_type;
39 template <
typename>
static void check(...);
42 static constexpr
bool value =
43 !std::is_void<decltype(check<T>(
nullptr))>::value && !is_map<T>::value;
47 template <
typename T,
size_t N>
48 auto range_begin(
const T (&arr)[N]) ->
const T* {
51 template <
typename T,
size_t N>
auto range_end(
const T (&arr)[N]) ->
const T* {
55 template <
typename T,
typename Enable =
void>
56 struct has_member_fn_begin_end_t : std::false_type {};
59 struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
60 decltype(std::declval<T>().end())>>
65 auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
66 return static_cast<T&&
>(rng).begin();
69 auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
70 return static_cast<T&&
>(rng).end();
76 auto range_begin(T&& rng)
77 -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
78 decltype(begin(static_cast<T&&>(rng)))> {
79 return begin(static_cast<T&&>(rng));
82 auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
83 decltype(end(static_cast<T&&>(rng)))> {
84 return end(static_cast<T&&>(rng));
87 template <
typename T,
typename Enable =
void>
88 struct has_const_begin_end : std::false_type {};
89 template <
typename T,
typename Enable =
void>
90 struct has_mutable_begin_end : std::false_type {};
93 struct has_const_begin_end<
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>&>()))>>
100 template <
typename T>
101 struct has_mutable_begin_end<
102 T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
103 decltype(detail::range_end(std::declval<T&>())),
106 int>> : std::true_type {};
108 template <
typename T,
typename _ =
void>
struct is_range_ : std::false_type {};
109 template <
typename T>
110 struct is_range_<T, void>
111 : std::integral_constant<bool, (has_const_begin_end<T>::value ||
112 has_mutable_begin_end<T>::value)> {};
115 template <
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(...);
121 static constexpr
bool value =
122 !std::is_void<decltype(check<T>(
nullptr))>::value;
126 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 127 template <
typename T, T... N>
128 using integer_sequence = std::integer_sequence<T, N...>;
129 template <
size_t... N>
using index_sequence = std::index_sequence<N...>;
130 template <
size_t N>
using make_index_sequence = std::make_index_sequence<N>;
132 template <
typename T, T... N>
struct integer_sequence {
133 using value_type = T;
135 static FMT_CONSTEXPR
auto size() ->
size_t {
return sizeof...(N); }
138 template <
size_t... N>
using index_sequence = integer_sequence<size_t, N...>;
140 template <
typename T,
size_t N, T... Ns>
141 struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
142 template <
typename T, T... Ns>
143 struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
146 using make_index_sequence = make_integer_sequence<size_t, N>;
149 template <
typename T>
150 using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
152 template <typename T, typename C, bool = is_tuple_like_<T>::value>
153 class is_tuple_formattable_ {
155 static constexpr
bool value =
false;
157 template <
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;
163 template <
size_t... Is>
164 static auto check(index_sequence<Is...>) -> decltype(all_true(
165 index_sequence<Is...>{},
166 integer_sequence<bool,
167 (is_formattable<typename std::tuple_element<Is, T>::type,
171 static constexpr
bool value =
172 decltype(check(tuple_index_sequence<T>{}))::value;
175 template <
typename Tuple,
typename F,
size_t... Is>
176 FMT_CONSTEXPR
void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
179 const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
180 ignore_unused(unused);
183 template <
typename Tuple,
typename F>
184 FMT_CONSTEXPR
void for_each(Tuple&& t, F&& f) {
185 for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
186 std::forward<Tuple>(t), std::forward<F>(f));
189 template <
typename Tuple1,
typename Tuple2,
typename F,
size_t... Is>
190 void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
192 const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
193 ignore_unused(unused);
196 template <
typename Tuple1,
typename Tuple2,
typename F>
197 void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
198 for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
199 std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
205 template <
typename Char,
typename... T>
206 using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
209 template <
typename Tuple,
typename Char,
size_t... Is>
210 auto get_formatters(index_sequence<Is...>)
211 -> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
214 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 216 template <
typename R>
struct range_reference_type_impl {
217 using type = decltype(*detail::range_begin(std::declval<R&>()));
220 template <
typename T,
size_t N>
struct range_reference_type_impl<T[N]> {
224 template <
typename T>
225 using range_reference_type =
typename range_reference_type_impl<T>::type;
227 template <
typename Range>
228 using range_reference_type =
229 decltype(*detail::range_begin(std::declval<Range&>()));
234 template <
typename Range>
235 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
237 template <
typename Formatter>
238 FMT_CONSTEXPR
auto maybe_set_debug_format(Formatter& f,
bool set)
239 -> decltype(f.set_debug_format(
set)) {
240 f.set_debug_format(
set);
242 template <
typename Formatter>
243 FMT_CONSTEXPR
void maybe_set_debug_format(Formatter&, ...) {}
245 template <
typename T>
246 struct range_format_kind_
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> {};
254 template <range_format K>
255 using range_format_constant = std::integral_constant<range_format, K>;
258 template <
typename Char>
struct parse_empty_specs {
259 template <
typename Formatter> FMT_CONSTEXPR
void operator()(Formatter& f) {
261 detail::maybe_set_debug_format(f,
true);
265 template <
typename FormatContext>
struct format_tuple_element {
266 using char_type =
typename FormatContext::char_type;
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));
283 template <
typename T>
struct is_tuple_like {
284 static constexpr
bool value =
285 detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
289 template <
typename T,
typename C>
struct is_tuple_formattable {
290 static constexpr
bool value = detail::is_tuple_formattable_<T, C>::value;
293 template <
typename Tuple,
typename Char>
294 struct formatter<Tuple, Char,
295 enable_if_t<fmt::is_tuple_like<Tuple>::value &&
296 fmt::is_tuple_formattable<Tuple, Char>::value>> {
298 decltype(detail::tuple::get_formatters<Tuple, Char>(
299 detail::tuple_index_sequence<Tuple>())) formatters_;
303 detail::string_literal<Char, '('>{};
305 detail::string_literal<Char, ')'>{};
308 FMT_CONSTEXPR formatter() {}
316 opening_bracket_ = open;
317 closing_bracket_ = close;
321 auto it = ctx.begin();
322 auto end = ctx.end();
323 if (it != end && detail::to_ascii(*it) ==
'n') {
325 set_brackets({}, {});
328 if (it != end && *it !=
'}') report_error(
"invalid format specifier");
330 detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
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()));
340 detail::format_tuple_element<FormatContext>{0, ctx, separator_});
341 return detail::copy<Char>(closing_bracket_, ctx.out());
346 template <
typename T,
typename Char>
struct is_range {
347 static constexpr
bool value =
348 detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
353 template <
typename Char,
typename Element>
354 using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
356 template <
typename R>
357 using maybe_const_range =
358 conditional_t<has_const_begin_end<R>::value,
const R, R>;
360 template <
typename R,
typename Char>
361 struct is_formattable_delayed
362 : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
365 template <
typename...>
struct conjunction : std::true_type {};
366 template <
typename P>
struct conjunction<P> : P {};
367 template <
typename P1,
typename... Pn>
368 struct conjunction<P1, Pn...>
369 : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
372 template <
typename T,
typename Char,
typename Enable =
void>
373 struct range_formatter;
375 template <
typename T,
typename Char>
376 struct range_formatter<
378 enable_if_t<conjunction<
std::is_same<T, remove_cvref_t<T>>,
379 is_formattable<T, Char>>::value>> {
381 detail::range_formatter_type<Char, T> underlying_;
384 detail::string_literal<Char, '['>{};
386 detail::string_literal<Char, ']'>{};
387 bool is_debug =
false;
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 {
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>(
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 {
407 FMT_CONSTEXPR range_formatter() {}
409 FMT_CONSTEXPR
auto underlying() -> detail::range_formatter_type<Char, T>& {
419 opening_bracket_ = open;
420 closing_bracket_ = close;
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);
429 switch (detail::to_ascii(*it)) {
431 set_brackets({}, {});
436 set_brackets({}, {});
438 if (it == end || *it !=
's') report_error(
"invalid format specifier");
441 if (!std::is_same<T, Char>::value)
442 report_error(
"invalid format specifier");
444 set_brackets(detail::string_literal<Char, '"'>{},
445 detail::string_literal<Char,
'"'>{});
447 detail::maybe_set_debug_format(underlying_,
false);
453 if (it != end && *it !=
'}') {
454 if (*it !=
':') report_error(
"invalid format specifier");
455 detail::maybe_set_debug_format(underlying_,
false);
460 return underlying_.parse(ctx);
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);
470 out = detail::copy<Char>(opening_bracket_, out);
472 for (; it != end; ++it) {
473 if (i > 0) out = detail::copy<Char>(separator_, out);
476 out = underlying_.format(item, ctx);
479 out = detail::copy<Char>(closing_bracket_, out);
485 template <
typename T,
typename Char,
typename Enable =
void>
486 struct range_format_kind
488 is_range<T, Char>::value, detail::range_format_kind_<T>,
489 std::integral_constant<range_format, range_format::disabled>> {};
491 template <
typename R,
typename Char>
494 enable_if_t<conjunction<
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 &&
499 range_format_kind<R, Char>::value != range_format::debug_string>,
500 detail::is_formattable_delayed<R, Char>>::value>> {
502 using range_type = detail::maybe_const_range<R>;
503 range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
506 using nonlocking = void;
508 FMT_CONSTEXPR formatter() {
509 if (detail::const_check(range_format_kind<R, Char>::value !=
512 range_formatter_.set_brackets(detail::string_literal<Char,
'{'>{},
513 detail::string_literal<Char, '}'>{});
517 return range_formatter_.parse(ctx);
520 template <
typename FormatContext>
521 auto format(range_type& range, FormatContext& ctx)
const 522 -> decltype(ctx.out()) {
523 return range_formatter_.format(range, ctx);
528 template <
typename R,
typename Char>
531 enable_if_t<conjunction<
532 bool_constant<range_format_kind<R, Char>::value == range_format::map>,
533 detail::is_formattable_delayed<R, Char>>::value>> {
535 using map_type = detail::maybe_const_range<R>;
536 using element_type = detail::uncvref_type<map_type>;
538 decltype(detail::tuple::get_formatters<element_type, Char>(
539 detail::tuple_index_sequence<element_type>())) formatters_;
540 bool no_delimiters_ = false;
543 FMT_CONSTEXPR formatter() {}
546 auto it = ctx.begin();
547 auto end = ctx.end();
549 if (detail::to_ascii(*it) ==
'n') {
550 no_delimiters_ =
true;
553 if (it != end && *it !=
'}') {
554 if (*it !=
':') report_error(
"invalid format specifier");
559 detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
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);
570 for (
auto&& value : map) {
571 if (i > 0) out = detail::copy<Char>(sep, out);
573 detail::for_each2(formatters_, value,
574 detail::format_tuple_element<FormatContext>{
575 0, ctx, detail::string_literal<Char, ':', ' '>{}});
579 if (!no_delimiters_) out = detail::copy<Char>(close, out);
585 template <
typename R,
typename Char>
588 enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
589 range_format_kind<R, Char>::value ==
590 range_format::debug_string>> {
592 using range_type = detail::maybe_const_range<R>;
594 conditional_t<std::is_constructible<
595 detail::std_string_view<Char>,
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>>;
600 formatter<string_type, Char> underlying_;
604 return underlying_.parse(ctx);
607 template <
typename FormatContext>
608 auto format(range_type& range, FormatContext& ctx)
const 609 -> decltype(ctx.out()) {
610 auto out = ctx.out();
611 if (detail::const_check(range_format_kind<R, Char>::value ==
612 range_format::debug_string))
614 out = underlying_.format(
615 string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
616 if (detail::const_check(range_format_kind<R, Char>::value ==
617 range_format::debug_string))
623 template <
typename It,
typename Sentinel,
typename Char =
char>
624 struct join_view : detail::view {
630 : begin(
std::move(b)), end(e), sep(s) {}
633 template <
typename It,
typename Sentinel,
typename Char>
634 struct formatter<join_view<It, Sentinel, Char>, Char> {
637 #ifdef __cpp_lib_ranges 638 std::iter_value_t<It>;
640 typename std::iterator_traits<It>::value_type;
642 formatter<remove_cvref_t<value_type>, Char> value_formatter_;
644 using view = conditional_t<std::is_copy_constructible<It>::value,
645 const join_view<It, Sentinel, Char>,
646 join_view<It, Sentinel, Char>>;
649 using nonlocking = void;
652 return value_formatter_.parse(ctx);
655 template <
typename FormatContext>
656 auto format(view& value, FormatContext& ctx)
const -> decltype(ctx.out()) {
658 conditional_t<std::is_copy_constructible<view>::value, It, It&>;
659 iter it = value.begin;
660 auto out = ctx.out();
661 if (it == value.end)
return out;
662 out = value_formatter_.format(*it, ctx);
664 while (it != value.end) {
665 out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
667 out = value_formatter_.format(*it, ctx);
675 template <
typename Tuple,
typename Char>
struct tuple_join_view : detail::view {
680 : tuple(t), sep{s} {}
686 #ifndef FMT_TUPLE_JOIN_SPECIFIERS 687 # define FMT_TUPLE_JOIN_SPECIFIERS 0 690 template <
typename Tuple,
typename Char>
691 struct formatter<tuple_join_view<Tuple, Char>, Char,
692 enable_if_t<is_tuple_like<Tuple>::value>> {
694 return do_parse(ctx, std::tuple_size<Tuple>());
697 template <
typename FormatContext>
698 auto format(
const tuple_join_view<Tuple, Char>& value,
699 FormatContext& ctx)
const ->
typename FormatContext::iterator {
700 return do_format(value, ctx, std::tuple_size<Tuple>());
704 decltype(detail::tuple::get_formatters<Tuple, Char>(
705 detail::tuple_index_sequence<Tuple>())) formatters_;
708 std::integral_constant<
size_t, 0>)
715 std::integral_constant<size_t, N>)
717 auto end = ctx.begin();
718 #if FMT_TUPLE_JOIN_SPECIFIERS 719 end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
721 auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
723 report_error(
"incompatible format specs for tuple elements");
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 {
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 {
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);
747 return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
754 template <
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(...);
759 static constexpr
bool value =
760 !std::is_void<decltype(check<T>(
nullptr))>::value;
763 template <
typename Container>
struct all {
765 auto begin() const -> typename Container::const_iterator {
return c.begin(); }
766 auto end() const -> typename Container::const_iterator {
return c.end(); }
770 template <
typename T,
typename 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> {
777 using all = detail::all<typename T::container_type>;
778 template <
typename FormatContext>
779 auto format(
const T& value, FormatContext& ctx)
const -> decltype(ctx.out()) {
781 static auto get(
const T& v) -> all {
782 return {v.*(&getter::c)};
785 return formatter<all>::format(
getter::get(value), ctx);
793 template <
typename It,
typename Sentinel>
794 auto join(It begin, Sentinel end,
string_view sep) -> join_view<It, Sentinel> {
795 return {std::move(begin), end, sep};
812 template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
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};
828 template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
830 -> tuple_join_view<Tuple, char> {
843 template <
typename T>
845 -> join_view<const T*, const T*> {
846 return join(std::begin(list), std::end(list), sep);
852 #endif // FMT_RANGES_H_ A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:785
BasicCoordinate< num_dimensions+1, coordT > join(const coordT &a, const BasicCoordinate< num_dimensions, coordT > &c)
make a longer BasicCoordinate, by prepending c with the single coordT
Definition: BasicCoordinate.inl:447
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:623
Converts a string literal into a format string that will be parsed at compile time and converted into...
Definition: args.h:20
const Array< num_dimensions - num_dimensions2, elemT > & get(const Array< num_dimensions, elemT > &a, const BasicCoordinate< num_dimensions2, int > &c)
an alternative for array indexing using BasicCoordinate objects
Definition: array_index_functions.inl:114