mirror of
https://github.com/fmtlib/fmt.git
synced 2026-09-13 14:22:26 +08:00
Refactor range outer formatting
This commit is contained in:
parent
61e3b92396
commit
d0aae8c15a
@ -4053,6 +4053,50 @@ FMT_CONSTEXPR auto native_formatter<T, Char, TYPE>::format(
|
||||
specs_.precision_ref, ctx);
|
||||
return write<Char>(ctx.out(), val, specs, ctx.locale());
|
||||
}
|
||||
|
||||
// Parses and applies the outer alignment and width of a nested value.
|
||||
template <typename Char> class nested_format_specs {
|
||||
private:
|
||||
format_specs specs_;
|
||||
arg_ref<Char> width_ref_;
|
||||
|
||||
public:
|
||||
constexpr nested_format_specs() : specs_(), width_ref_() {}
|
||||
|
||||
FMT_CONSTEXPR auto parse(const Char* begin, const Char* end,
|
||||
parse_context<Char>& ctx) -> const Char* {
|
||||
if (begin == end || *begin == '}') return begin;
|
||||
begin = parse_align(begin, end, specs_);
|
||||
if (begin == end) return begin;
|
||||
Char c = *begin;
|
||||
if ((c >= '0' && c <= '9') || c == '{')
|
||||
begin = parse_width(begin, end, specs_, width_ref_, ctx);
|
||||
return begin;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto parse(const Char* begin, const Char* end,
|
||||
parse_context<Char>& ctx, Char separator)
|
||||
-> const Char* {
|
||||
// A separator introduces the nested spec and is never a fill character.
|
||||
if (begin != end && *begin == separator) return begin;
|
||||
return parse(begin, end, ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename F, typename... T>
|
||||
FMT_CONSTEXPR auto write(FormatContext& ctx, const F& f, T&&... values) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto specs = specs_;
|
||||
handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx);
|
||||
if (specs.width == 0) return f.write_body(ctx, static_cast<T&&>(values)...);
|
||||
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
auto buffer_ctx =
|
||||
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
|
||||
f.write_body(buffer_ctx, static_cast<T&&>(values)...);
|
||||
return detail::write<Char>(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
@ -4298,8 +4342,7 @@ template <typename T> struct nested_view {
|
||||
|
||||
template <typename U, typename Char = char> struct nested_formatter {
|
||||
private:
|
||||
format_specs specs_;
|
||||
detail::arg_ref<Char> width_ref_;
|
||||
detail::nested_format_specs<Char> specs_;
|
||||
formatter<U, Char> formatter_;
|
||||
|
||||
template <typename FormatContext>
|
||||
@ -4315,23 +4358,21 @@ template <typename U, typename Char = char> struct nested_formatter {
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename... T>
|
||||
auto write_args(FormatContext& ctx, const T&... args) const
|
||||
auto write_body(FormatContext& ctx, const T&... args) const
|
||||
-> decltype(ctx.out()) {
|
||||
FMT_APPLY_VARIADIC(ctx.advance_to(write_arg(ctx, args)));
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
friend class detail::nested_format_specs<Char>;
|
||||
|
||||
public:
|
||||
constexpr nested_formatter() : specs_(), width_ref_(), formatter_() {}
|
||||
constexpr nested_formatter() : specs_(), formatter_() {}
|
||||
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
if (it == end) return it;
|
||||
it = detail::parse_align(it, end, specs_);
|
||||
if (it == end) return it;
|
||||
Char c = *it;
|
||||
if ((c >= '0' && c <= '9') || c == '{')
|
||||
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
|
||||
it = specs_.parse(it, end, ctx);
|
||||
ctx.advance_to(it);
|
||||
return formatter_.parse(ctx);
|
||||
}
|
||||
@ -4339,17 +4380,7 @@ template <typename U, typename Char = char> struct nested_formatter {
|
||||
template <typename FormatContext, typename... T>
|
||||
auto write(FormatContext& ctx, const T&... args) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto specs = specs_;
|
||||
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
||||
ctx);
|
||||
if (specs.width == 0) return write_args(ctx, args...);
|
||||
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
auto buffer_ctx =
|
||||
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
|
||||
write_args(buffer_ctx, args...);
|
||||
return detail::write<Char>(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||
return specs_.write(ctx, *this, args...);
|
||||
}
|
||||
|
||||
auto nested(const U& value) const -> nested_view<U> { return {&value}; }
|
||||
|
||||
@ -297,37 +297,6 @@ template <typename T, typename C> struct is_tuple_formattable {
|
||||
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// The fill, alignment and width that a range, tuple or map format spec may
|
||||
// begin with. They apply to the composed output rather than to the elements.
|
||||
template <typename Char> struct composed_specs {
|
||||
format_specs specs;
|
||||
arg_ref<Char> width_ref;
|
||||
|
||||
FMT_CONSTEXPR auto parse(const Char* it, const Char* end,
|
||||
parse_context<Char>& ctx) -> const Char* {
|
||||
// A leading ':' introduces the underlying spec, so it is never a fill.
|
||||
if (it == end || *it == '}' || *it == ':') return it;
|
||||
it = parse_align(it, end, specs);
|
||||
if (it == end) return it;
|
||||
Char c = *it;
|
||||
if ((c >= '0' && c <= '9') || c == '{')
|
||||
it = parse_width(it, end, specs, width_ref, ctx);
|
||||
return it;
|
||||
}
|
||||
|
||||
// Resolves a dynamic width. A width of 0 means no padding is needed.
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto resolve(FormatContext& ctx) const -> format_specs {
|
||||
auto s = specs;
|
||||
handle_dynamic_spec(s.dynamic_width(), s.width, width_ref, ctx);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Tuple, typename Char>
|
||||
struct formatter<Tuple, Char,
|
||||
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
|
||||
@ -341,10 +310,10 @@ struct formatter<Tuple, Char,
|
||||
detail::string_literal<Char, '('>{};
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ')'>{};
|
||||
detail::composed_specs<Char> composed_;
|
||||
detail::nested_format_specs<Char> specs_;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto write_body(const Tuple& value, FormatContext& ctx) const
|
||||
auto write_body(FormatContext& ctx, const Tuple& value) const
|
||||
-> decltype(ctx.out()) {
|
||||
ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
|
||||
detail::for_each2(
|
||||
@ -353,6 +322,8 @@ struct formatter<Tuple, Char,
|
||||
return detail::copy<Char>(closing_bracket_, ctx.out());
|
||||
}
|
||||
|
||||
friend class detail::nested_format_specs<Char>;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR formatter() {}
|
||||
|
||||
@ -369,7 +340,7 @@ struct formatter<Tuple, Char,
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
auto it = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
it = composed_.parse(it, end, ctx);
|
||||
it = specs_.parse(it, end, ctx, ':');
|
||||
if (it != end && detail::to_ascii(*it) == 'n') {
|
||||
++it;
|
||||
set_brackets({}, {});
|
||||
@ -384,14 +355,7 @@ struct formatter<Tuple, Char,
|
||||
template <typename FormatContext>
|
||||
auto format(const Tuple& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto specs = composed_.resolve(ctx);
|
||||
if (specs.width == 0) return write_body(value, ctx);
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
auto nested_ctx =
|
||||
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
|
||||
write_body(value, nested_ctx);
|
||||
return detail::write<Char>(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||
return specs_.write(ctx, *this, value);
|
||||
}
|
||||
};
|
||||
|
||||
@ -439,7 +403,7 @@ struct range_formatter<
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ']'>{};
|
||||
bool is_debug = false;
|
||||
detail::composed_specs<Char> composed_;
|
||||
detail::nested_format_specs<Char> specs_;
|
||||
|
||||
template <typename Output, typename It, typename Sentinel, typename U = T,
|
||||
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
|
||||
@ -458,6 +422,8 @@ struct range_formatter<
|
||||
return out;
|
||||
}
|
||||
|
||||
friend class detail::nested_format_specs<Char>;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR range_formatter() {}
|
||||
|
||||
@ -481,7 +447,7 @@ struct range_formatter<
|
||||
detail::maybe_set_debug_format(underlying_, true);
|
||||
if (it == end) return underlying_.parse(ctx);
|
||||
|
||||
it = composed_.parse(it, end, ctx);
|
||||
it = specs_.parse(it, end, ctx, ':');
|
||||
if (it == end) {
|
||||
ctx.advance_to(it);
|
||||
return underlying_.parse(ctx);
|
||||
@ -524,18 +490,11 @@ struct range_formatter<
|
||||
template <typename R, typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto specs = composed_.resolve(ctx);
|
||||
if (specs.width == 0) return write_body(range, ctx);
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
auto nested_ctx =
|
||||
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
|
||||
write_body(range, nested_ctx);
|
||||
return detail::write<Char>(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||
return specs_.write(ctx, *this, range);
|
||||
}
|
||||
|
||||
template <typename R, typename FormatContext>
|
||||
FMT_CONSTEXPR auto write_body(R&& range, FormatContext& ctx) const
|
||||
FMT_CONSTEXPR auto write_body(FormatContext& ctx, R&& range) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
auto it = detail::range_begin(range);
|
||||
@ -612,7 +571,9 @@ struct formatter<
|
||||
decltype(detail::tuple::get_formatters<element_type, Char>(
|
||||
detail::tuple_index_sequence<element_type>())) formatters_;
|
||||
bool no_delimiters_ = false;
|
||||
detail::composed_specs<Char> composed_;
|
||||
detail::nested_format_specs<Char> specs_;
|
||||
|
||||
friend class detail::nested_format_specs<Char>;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR formatter() {}
|
||||
@ -621,7 +582,7 @@ struct formatter<
|
||||
auto it = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
if (it != end) {
|
||||
it = composed_.parse(it, end, ctx);
|
||||
it = specs_.parse(it, end, ctx, ':');
|
||||
if (it != end && detail::to_ascii(*it) == 'n') {
|
||||
no_delimiters_ = true;
|
||||
++it;
|
||||
@ -638,18 +599,11 @@ struct formatter<
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
auto specs = composed_.resolve(ctx);
|
||||
if (specs.width == 0) return write_body(map, ctx);
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
auto nested_ctx =
|
||||
FormatContext(basic_appender<Char>(buf), ctx.args(), ctx.locale());
|
||||
write_body(map, nested_ctx);
|
||||
return detail::write<Char>(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||
return specs_.write(ctx, *this, map);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto write_body(map_type& map, FormatContext& ctx) const
|
||||
auto write_body(FormatContext& ctx, map_type& map) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user