mirror of
https://github.com/fmtlib/fmt.git
synced 2026-09-13 14:22:26 +08:00
Simplify nested_formatter context handling
Format nested values directly through the stored formatter so dynamic specifications resolve against the enclosing operation.
This commit is contained in:
parent
c5728fb720
commit
cbb264122a
@ -181,10 +181,7 @@ For example:
|
||||
template <>
|
||||
struct fmt::formatter<point> : nested_formatter<double> {
|
||||
auto format(point p, format_context& ctx) const {
|
||||
return write_padded(ctx, [=](auto out) {
|
||||
return format_to(out, "({}, {})", this->nested(p.x),
|
||||
this->nested(p.y));
|
||||
});
|
||||
return write(ctx, "(", nested(p.x), ", ", nested(p.y), ")");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -4064,6 +4064,9 @@ template <typename OutputIt, typename Char> class generic_context {
|
||||
constexpr auto arg_id(basic_string_view<Char> name) const -> int {
|
||||
return args_.get_id(name);
|
||||
}
|
||||
auto args() const -> const basic_format_args<generic_context>& {
|
||||
return args_;
|
||||
}
|
||||
|
||||
constexpr auto out() const -> iterator { return out_; }
|
||||
|
||||
@ -4264,64 +4267,67 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct nested_view {
|
||||
const formatter<T, Char>* fmt;
|
||||
template <typename T> struct nested_view {
|
||||
const T* value;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<nested_view<T, Char>, Char> {
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
return ctx.begin();
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(nested_view<T, Char> view, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return view.fmt->format(*view.value, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char = char> struct nested_formatter {
|
||||
template <typename U, typename Char = char> struct nested_formatter {
|
||||
private:
|
||||
basic_specs specs_;
|
||||
int width_;
|
||||
formatter<T, Char> formatter_;
|
||||
format_specs specs_;
|
||||
detail::arg_ref<Char> width_ref_;
|
||||
formatter<U, Char> formatter_;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto write_arg(FormatContext& ctx, nested_view<U> view) const
|
||||
-> decltype(ctx.out()) {
|
||||
return formatter_.format(*view.value, ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename V>
|
||||
auto write_arg(FormatContext& ctx, const V& value) const
|
||||
-> decltype(ctx.out()) {
|
||||
return detail::write<Char>(ctx.out(), value);
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename... T>
|
||||
auto write_args(FormatContext& ctx, const T&... args) const
|
||||
-> decltype(ctx.out()) {
|
||||
FMT_APPLY_VARIADIC(ctx.advance_to(write_arg(ctx, args)));
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr nested_formatter() : width_(0) {}
|
||||
constexpr nested_formatter() : specs_(), width_ref_(), formatter_() {}
|
||||
|
||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
if (it == end) return it;
|
||||
auto specs = format_specs();
|
||||
it = detail::parse_align(it, end, specs);
|
||||
specs_ = specs;
|
||||
it = detail::parse_align(it, end, specs_);
|
||||
if (it == end) return it;
|
||||
Char c = *it;
|
||||
auto width_ref = detail::arg_ref<Char>();
|
||||
if ((c >= '0' && c <= '9') || c == '{') {
|
||||
it = detail::parse_width(it, end, specs, width_ref, ctx);
|
||||
width_ = specs.width;
|
||||
}
|
||||
if ((c >= '0' && c <= '9') || c == '{')
|
||||
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
|
||||
ctx.advance_to(it);
|
||||
return formatter_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename F>
|
||||
auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) {
|
||||
if (width_ == 0) return write(ctx.out());
|
||||
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>();
|
||||
write(basic_appender<Char>(buf));
|
||||
auto specs = format_specs();
|
||||
specs.width = width_;
|
||||
specs.copy_fill_from(specs_);
|
||||
specs.set_align(specs_.align());
|
||||
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);
|
||||
}
|
||||
|
||||
auto nested(const T& value) const -> nested_view<T, Char> {
|
||||
return nested_view<T, Char>{&formatter_, &value};
|
||||
}
|
||||
auto nested(const U& value) const -> nested_view<U> { return {&value}; }
|
||||
};
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
@ -371,24 +371,13 @@ class path : public std::filesystem::path {
|
||||
template <size_t N, typename Char>
|
||||
struct formatter<std::bitset<N>, Char>
|
||||
: nested_formatter<basic_string_view<Char>, Char> {
|
||||
private:
|
||||
// This is a functor because C++11 doesn't support generic lambdas.
|
||||
struct writer {
|
||||
const std::bitset<N>& bs;
|
||||
|
||||
template <typename OutputIt>
|
||||
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
|
||||
for (auto pos = N; pos > 0; --pos)
|
||||
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename FormatContext>
|
||||
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return this->write_padded(ctx, writer{bs});
|
||||
auto str = bs.template to_string<Char>();
|
||||
auto view = basic_string_view<Char>(str);
|
||||
return this->write(ctx, this->nested(view));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -2066,7 +2066,6 @@ TEST(format_test, group_digits_view) {
|
||||
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(-100)), " -100");
|
||||
}
|
||||
|
||||
#ifdef __cpp_generic_lambdas
|
||||
struct point {
|
||||
double x, y;
|
||||
};
|
||||
@ -2074,18 +2073,19 @@ struct point {
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<point> : nested_formatter<double> {
|
||||
auto format(point p, format_context& ctx) const -> decltype(ctx.out()) {
|
||||
return write_padded(ctx, [this, p](auto out) -> decltype(out) {
|
||||
return fmt::format_to(out, "({}, {})", this->nested(p.x),
|
||||
this->nested(p.y));
|
||||
});
|
||||
return write(ctx, "(", nested(p.x), ", ", nested(p.y), ")");
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(format_test, nested_formatter) {
|
||||
EXPECT_EQ(fmt::format("{:>16.2f}", point{1, 2}), " (1.00, 2.00)");
|
||||
EXPECT_EQ(fmt::format("{:.{}f}", point{1.234, 5.678}, 2), "(1.23, 5.68)");
|
||||
EXPECT_EQ(fmt::format("{:>20.{}f}", point{1.234, 5.678}, 2),
|
||||
" (1.23, 5.68)");
|
||||
EXPECT_EQ(fmt::format("{:.{}f}", point{1.2344, 67.8901}, 3),
|
||||
"(1.234, 67.890)");
|
||||
}
|
||||
#endif // __cpp_generic_lambdas
|
||||
|
||||
enum test_enum { foo, bar };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user