Files
fmt/include/fmt/ranges.h
T

870 lines
28 KiB
C++
Raw Normal View History

// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#ifndef FMT_MODULE
2024-04-10 20:48:32 +02:00
# include <initializer_list>
# include <iterator>
# include <tuple>
# include <type_traits>
# include <utility>
2024-04-10 20:48:32 +02:00
#endif
2020-03-14 07:41:08 -07:00
2024-03-07 13:51:46 -05:00
#include "format.h"
2025-10-02 12:30:55 -04:00
#if FMT_HAS_CPP_ATTRIBUTE(clang::lifetimebound)
# define FMT_LIFETIMEBOUND [[clang::lifetimebound]]
#else
# define FMT_LIFETIMEBOUND
#endif
2025-10-02 16:24:22 -04:00
FMT_PRAGMA_CLANG(diagnostic error "-Wreturn-stack-address")
2025-10-02 12:30:55 -04:00
FMT_BEGIN_NAMESPACE
FMT_EXPORT
2024-05-04 07:15:15 -07:00
enum class range_format { disabled, map, set, sequence, string, debug_string };
2020-05-10 07:25:42 -07:00
namespace detail {
2021-12-18 07:12:53 -08:00
template <typename T> class is_map {
2021-12-18 07:35:40 -08:00
template <typename U> static auto check(U*) -> typename U::mapped_type;
2021-12-18 07:12:53 -08:00
template <typename> static void check(...);
public:
2025-05-12 10:41:58 -07:00
static constexpr bool value =
2021-12-18 07:12:53 -08:00
!std::is_void<decltype(check<T>(nullptr))>::value;
};
2021-12-18 07:35:40 -08:00
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
2025-05-12 10:41:58 -07:00
static constexpr bool value =
2021-12-18 07:35:40 -08:00
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
2020-10-24 11:25:29 +02:00
// C array overload
2025-05-04 13:02:02 -07:00
template <typename T, size_t N>
2020-10-24 11:25:29 +02:00
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
2025-05-04 13:02:02 -07:00
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
2020-10-24 11:25:29 +02:00
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
2020-10-24 11:25:29 +02:00
decltype(std::declval<T>().end())>>
: std::true_type {};
2024-04-22 15:45:20 -07:00
// Member function overloads.
2020-10-24 11:25:29 +02:00
template <typename T>
FMT_CONSTEXPR auto range_begin(T&& rng)
-> decltype(static_cast<T&&>(rng).begin()) {
2024-09-06 13:20:44 -07:00
return static_cast<T&&>(rng).begin();
}
2020-10-24 11:25:29 +02:00
template <typename T>
FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
2024-09-06 13:20:44 -07:00
return static_cast<T&&>(rng).end();
}
2020-10-24 11:25:29 +02:00
2024-04-22 15:45:20 -07:00
// ADL overloads. Only participate in overload resolution if member functions
2020-10-24 11:25:29 +02:00
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(*detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(
std::declval<const remove_cvref_t<T>&>()))>>
2020-10-24 11:25:29 +02:00
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types
2023-02-10 09:45:27 -08:00
int>> : std::true_type {};
2020-10-24 11:25:29 +02:00
2024-09-06 13:20:44 -07:00
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
2020-10-24 11:25:29 +02:00
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
2021-12-29 08:00:36 -08:00
// tuple_size and tuple_element check.
2019-01-12 18:27:38 -08:00
template <typename T> class is_tuple_like_ {
2024-09-20 07:08:47 -07:00
template <typename U, typename V = typename std::remove_cv<U>::type>
static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
2019-01-12 18:27:38 -08:00
template <typename> static void check(...);
public:
2025-05-12 10:41:58 -07:00
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T, typename _ = void>
struct is_optional_like_ : std::false_type {};
template <typename T>
struct is_optional_like_<T, void_t<decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().value())>>
: std::true_type {};
// Check for integer_sequence
2022-05-29 15:01:43 -07:00
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
2020-05-07 15:59:46 -07:00
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
2018-05-20 09:09:03 -07:00
#else
2019-01-12 18:27:38 -08:00
template <typename T, T... N> struct integer_sequence {
2019-07-07 16:43:38 -07:00
using value_type = T;
2023-12-19 17:51:41 -08:00
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
2020-05-07 15:59:46 -07:00
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
2020-05-07 15:59:46 -07:00
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
2020-05-07 15:59:46 -07:00
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
2018-05-20 09:09:03 -07:00
#endif
template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
2025-05-12 10:41:58 -07:00
static constexpr bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
2024-01-29 07:48:48 -08:00
template <size_t... Is>
static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto all_true(...) -> std::false_type;
template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
2022-09-03 06:35:55 -07:00
index_sequence<Is...>{},
2023-12-19 17:51:41 -08:00
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
2025-05-12 10:41:58 -07:00
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
2023-02-18 10:23:42 -08:00
template <typename Tuple, typename F, size_t... Is>
2023-02-19 09:24:29 -08:00
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get;
2023-02-19 09:24:29 -08:00
// Using a free function get<Is>(Tuple) now.
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
2022-12-26 06:57:21 -08:00
ignore_unused(unused);
}
2023-02-19 09:24:29 -08:00
template <typename Tuple, typename F>
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
std::forward<Tuple>(t), std::forward<F>(f));
2019-01-12 18:27:38 -08:00
}
2023-02-19 09:24:29 -08:00
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
}
2023-02-19 09:24:29 -08:00
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
2025-05-04 13:02:02 -07:00
template <typename Tuple, typename Char, size_t... Is>
2023-02-19 09:24:29 -08:00
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
2022-01-08 09:52:39 -08:00
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
2025-05-04 13:02:02 -07:00
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range>
using range_reference_type =
decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
2022-01-08 09:52:39 -08:00
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
2024-05-04 07:15:15 -07:00
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
2023-02-19 09:24:29 -08:00
// These are not generic lambdas for compatibility with C++11.
2024-09-01 14:32:55 -07:00
template <typename Char> struct parse_empty_specs {
2023-02-19 09:24:29 -08:00
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
2024-09-01 14:32:55 -07:00
parse_context<Char>& ctx;
2023-02-19 09:24:29 -08:00
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
2023-02-19 09:24:29 -08:00
template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) {
2024-01-13 09:21:42 -08:00
if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
2023-02-19 09:24:29 -08:00
ctx.advance_to(f.format(v, ctx));
++i;
}
2023-02-19 09:24:29 -08:00
int i;
FormatContext& ctx;
basic_string_view<char_type> separator;
};
2020-05-10 07:25:42 -07:00
} // namespace detail
FMT_EXPORT
2019-01-12 18:27:38 -08:00
template <typename T> struct is_tuple_like {
2025-05-12 10:41:58 -07:00
static constexpr bool value =
2020-05-10 07:25:42 -07:00
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
2025-05-12 10:41:58 -07:00
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
2022-12-25 19:54:48 -08:00
template <typename Tuple, typename Char>
struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<Tuple, Char>::value>> {
2019-01-12 18:27:38 -08:00
private:
2023-02-19 09:24:29 -08:00
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
2022-07-29 15:55:16 -05:00
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{};
2019-01-12 18:27:38 -08:00
public:
2022-07-29 15:55:16 -05:00
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2023-02-19 09:24:29 -08:00
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && detail::to_ascii(*it) == 'n') {
++it;
set_brackets({}, {});
set_separator({});
}
if (it != end && *it != '}') report_error("invalid format specifier");
ctx.advance_to(it);
2024-09-01 14:32:55 -07:00
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
2023-02-19 09:24:29 -08:00
return it;
}
2023-02-18 09:39:58 -08:00
template <typename FormatContext>
2022-12-25 19:54:48 -08:00
auto format(const Tuple& value, FormatContext& ctx) const
2022-02-10 23:51:32 +05:00
-> decltype(ctx.out()) {
2024-01-13 09:21:42 -08:00
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
2023-02-19 09:24:29 -08:00
detail::for_each2(
formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
2024-01-13 09:21:42 -08:00
return detail::copy<Char>(closing_bracket_, ctx.out());
}
};
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr bool value = detail::is_range_<T>::value &&
!detail::is_optional_like_<T>::value &&
!detail::has_to_string_view<T>::value;
};
namespace detail {
2024-09-01 19:35:00 -07:00
template <typename Char, typename Element>
2024-09-16 20:24:30 -07:00
using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
2022-07-31 00:46:31 +05:00
template <typename R, typename Char>
struct is_formattable_delayed
2023-03-26 08:40:12 -07:00
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
} // namespace detail
2023-12-23 14:35:11 -08:00
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
2022-07-29 15:55:16 -05:00
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
2022-07-29 15:55:16 -05:00
template <typename T, typename Char>
struct range_formatter<
T, Char,
2023-03-26 08:40:12 -07:00
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
is_formattable<T, Char>>::value>> {
2022-07-29 15:55:16 -05:00
private:
detail::range_formatter_type<Char, T> underlying_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{};
2024-03-07 13:51:46 -05:00
bool is_debug = false;
2022-07-29 15:55:16 -05:00
2024-05-04 07:38:58 -07:00
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
2024-08-11 10:13:17 -07:00
specs.set_type(presentation_type::debug);
2024-05-04 07:38:58 -07:00
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
2022-07-29 15:55:16 -05:00
public:
2023-02-18 09:39:58 -08:00
FMT_CONSTEXPR range_formatter() {}
2022-07-29 15:55:16 -05:00
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
return underlying_;
}
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
separator_ = sep;
}
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
basic_string_view<Char> close) {
opening_bracket_ = open;
closing_bracket_ = close;
}
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
2024-03-07 13:51:46 -05:00
detail::maybe_set_debug_format(underlying_, true);
2024-03-09 08:20:27 -08:00
if (it == end) return underlying_.parse(ctx);
2024-03-07 13:51:46 -05:00
switch (detail::to_ascii(*it)) {
case 'n':
2022-07-29 15:55:16 -05:00
set_brackets({}, {});
2022-07-12 12:08:38 -05:00
++it;
2024-03-07 13:51:46 -05:00
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
2024-03-09 08:20:27 -08:00
if (it == end || *it != 's') report_error("invalid format specifier");
2024-03-07 13:51:46 -05:00
FMT_FALLTHROUGH;
case 's':
2024-03-09 08:20:27 -08:00
if (!std::is_same<T, Char>::value)
2024-03-07 13:51:46 -05:00
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
2022-07-12 12:08:38 -05:00
}
2023-02-18 09:39:58 -08:00
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
2024-03-07 13:51:46 -05:00
detail::maybe_set_debug_format(underlying_, false);
2023-02-18 09:39:58 -08:00
++it;
}
2022-07-29 15:55:16 -05:00
ctx.advance_to(it);
return underlying_.parse(ctx);
}
2023-02-18 10:23:42 -08:00
template <typename R, typename FormatContext>
FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
2021-12-18 07:35:40 -08:00
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
2024-07-09 16:46:34 +01:00
if (is_debug) return write_debug_string(out, std::move(it), end);
2024-03-07 13:51:46 -05:00
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) {
2024-01-13 09:21:42 -08:00
if (i > 0) out = detail::copy<Char>(separator_, out);
2022-07-29 15:55:16 -05:00
ctx.advance_to(out);
2024-05-23 20:39:11 +01:00
auto&& item = *it; // Need an lvalue
2024-09-16 20:24:30 -07:00
out = underlying_.format(item, ctx);
2021-05-04 20:53:56 -07:00
++i;
}
2024-01-13 09:21:42 -08:00
out = detail::copy<Char>(closing_bracket_, out);
2021-12-18 07:35:40 -08:00
return out;
}
};
FMT_EXPORT
2022-07-29 15:55:16 -05:00
template <typename T, typename Char, typename Enable = void>
struct range_format_kind
: conditional_t<
is_range<T, Char>::value, detail::range_format_kind_<T>,
std::integral_constant<range_format, range_format::disabled>> {};
template <typename R, typename Char>
2021-12-18 07:12:53 -08:00
struct formatter<
2022-07-29 15:55:16 -05:00
R, Char,
2024-05-04 09:18:35 -07:00
enable_if_t<conjunction<
bool_constant<
range_format_kind<R, Char>::value != range_format::disabled &&
range_format_kind<R, Char>::value != range_format::map &&
range_format_kind<R, Char>::value != range_format::string &&
2024-11-15 07:55:59 -08:00
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
2024-05-04 07:15:15 -07:00
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
2024-06-23 06:51:46 -07:00
using nonlocking = void;
2024-05-04 07:15:15 -07:00
FMT_CONSTEXPR formatter() {
2026-01-29 17:29:33 -08:00
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value != range_format::set)
return;
2024-05-04 09:18:35 -07:00
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
2024-05-04 07:15:15 -07:00
}
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2024-05-04 07:15:15 -07:00
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(range_type& range, FormatContext& ctx) const
2024-05-04 07:15:15 -07:00
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
2021-12-18 07:12:53 -08:00
};
// A map formatter.
2024-05-04 09:18:35 -07:00
template <typename R, typename Char>
struct formatter<
R, Char,
2025-01-25 10:31:07 -06:00
enable_if_t<conjunction<
bool_constant<range_format_kind<R, Char>::value == range_format::map>,
detail::is_formattable_delayed<R, Char>>::value>> {
2024-05-04 09:18:35 -07:00
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
2024-05-04 09:18:35 -07:00
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
2024-05-04 09:18:35 -07:00
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2024-05-04 09:18:35 -07:00
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
2024-05-04 09:18:35 -07:00
}
2024-09-01 14:32:55 -07:00
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
2024-05-04 09:18:35 -07:00
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
2024-05-04 11:21:34 -07:00
for (auto&& value : map) {
2024-05-04 09:18:35 -07:00
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
2024-09-16 20:24:30 -07:00
detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
2024-05-04 09:18:35 -07:00
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
2026-01-29 17:29:33 -08:00
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string) {
*out++ = '"';
2026-01-29 17:29:33 -08:00
}
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
2026-01-29 17:29:33 -08:00
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string)
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
2024-11-14 17:06:30 +00:00
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
2024-05-08 15:19:19 -07:00
public:
2024-05-09 17:26:26 -07:00
using nonlocking = void;
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(view& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
2024-11-14 17:06:30 +00:00
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
2024-05-08 15:19:19 -07:00
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
FMT_EXPORT
2025-05-04 15:23:48 -07:00
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
2025-05-04 15:23:48 -07:00
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const ->
typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
2024-09-01 14:32:55 -07:00
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, 0>)
2024-09-01 14:32:55 -07:00
-> const Char* {
return ctx.begin();
}
2024-09-01 14:32:55 -07:00
template <size_t N>
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>)
2024-09-01 14:32:55 -07:00
-> const Char* {
2021-12-29 14:46:45 -08:00
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) {
2021-08-23 10:10:56 -07:00
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
report_error("incompatible format specs for tuple elements");
}
#endif
return end;
}
template <typename FormatContext>
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>&,
FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
2021-06-02 17:06:02 -07:00
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
2021-06-02 17:06:02 -07:00
typename FormatContext::iterator {
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
2024-01-13 09:44:59 -08:00
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
}
};
2026-01-31 19:35:50 -08:00
namespace detail {
2023-02-10 09:45:27 -08:00
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
2023-04-06 17:31:07 +02:00
auto end() const -> typename Container::const_iterator { return c.end(); }
2023-02-10 09:45:27 -08:00
};
} // namespace detail
2026-01-30 20:09:00 +05:30
/**
2026-01-31 19:35:50 -08:00
* Specifies if `T` is a container adaptor (like `std::stack`) that should be
* formatted as the underlying container.
*/
2026-01-30 20:09:00 +05:30
FMT_EXPORT
2026-02-02 10:44:37 -08:00
template <typename T> struct is_container_adaptor {
2026-01-31 19:35:50 -08:00
private:
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
2026-01-30 20:09:00 +05:30
template <typename T, typename Char>
struct formatter<
T, Char,
2026-01-30 20:09:00 +05:30
enable_if_t<conjunction<is_container_adaptor<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
2023-02-10 09:45:27 -08:00
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
2025-03-15 10:11:46 -07:00
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
2025-03-15 10:11:46 -07:00
static auto get(const T& v) -> all {
return {v.*(&getter::c)}; // Access c through the derived class.
}
};
2025-03-15 10:11:46 -07:00
return formatter<all>::format(getter::get(value), ctx);
}
};
2023-04-10 09:43:56 -07:00
FMT_BEGIN_EXPORT
2021-04-16 20:04:55 +02:00
2024-11-15 21:01:59 +00:00
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/**
* Returns a view that formats `range` with elements separated by `sep`.
*
* **Example**:
*
* auto v = std::vector<int>{1, 2, 3};
* fmt::print("{}", fmt::join(v, ", "));
* // Output: 1, 2, 3
*
* `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
FMT_CONSTEXPR auto join(Range&& r, string_view sep)
2024-11-15 21:01:59 +00:00
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
}
/**
2024-06-02 13:17:04 -07:00
* Returns an object that formats `std::tuple` with elements separated by `sep`.
*
* **Example**:
*
2025-10-02 12:30:55 -04:00
* auto t = std::tuple<int, char>(1, 'a');
2024-06-02 13:17:04 -07:00
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
2025-10-02 12:30:55 -04:00
FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
2025-05-04 15:23:48 -07:00
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}
/**
2024-06-02 13:17:04 -07:00
* Returns an object that formats `std::initializer_list` with elements
* separated by `sep`.
*
* **Example**:
*
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
* // Output: "1, 2, 3"
*/
template <typename T>
2026-03-05 16:32:50 -08:00
FMT_DEPRECATED auto join(std::initializer_list<T> list, string_view sep)
2021-06-02 17:06:02 -07:00
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
2023-04-10 09:43:56 -07:00
FMT_END_EXPORT
FMT_END_NAMESPACE
2019-01-12 18:27:38 -08:00
#endif // FMT_RANGES_H_