diff --git a/doc/api.md b/doc/api.md index a8e8da09..51ab10b5 100644 --- a/doc/api.md +++ b/doc/api.md @@ -181,10 +181,7 @@ For example: template <> struct fmt::formatter : nested_formatter { 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), ")"); } }; diff --git a/include/fmt/format.h b/include/fmt/format.h index f886e96c..163745ea 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4064,6 +4064,9 @@ template class generic_context { constexpr auto arg_id(basic_string_view name) const -> int { return args_.get_id(name); } + auto args() const -> const basic_format_args& { + return args_; + } constexpr auto out() const -> iterator { return out_; } @@ -4264,64 +4267,67 @@ template struct formatter> : formatter { } }; -template struct nested_view { - const formatter* fmt; +template struct nested_view { const T* value; }; -template -struct formatter, Char> { - FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { - return ctx.begin(); - } - template - auto format(nested_view view, FormatContext& ctx) const - -> decltype(ctx.out()) { - return view.fmt->format(*view.value, ctx); - } -}; - -template struct nested_formatter { +template struct nested_formatter { private: - basic_specs specs_; - int width_; - formatter formatter_; + format_specs specs_; + detail::arg_ref width_ref_; + formatter formatter_; + + template + auto write_arg(FormatContext& ctx, nested_view view) const + -> decltype(ctx.out()) { + return formatter_.format(*view.value, ctx); + } + + template + auto write_arg(FormatContext& ctx, const V& value) const + -> decltype(ctx.out()) { + return detail::write(ctx.out(), value); + } + + template + 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& 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(); - 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 - auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) { - if (width_ == 0) return write(ctx.out()); + template + 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(); - write(basic_appender(buf)); - auto specs = format_specs(); - specs.width = width_; - specs.copy_fill_from(specs_); - specs.set_align(specs_.align()); + auto buffer_ctx = + FormatContext(basic_appender(buf), ctx.args(), ctx.locale()); + write_args(buffer_ctx, args...); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } - auto nested(const T& value) const -> nested_view { - return nested_view{&formatter_, &value}; - } + auto nested(const U& value) const -> nested_view { return {&value}; } }; inline namespace literals { diff --git a/include/fmt/std.h b/include/fmt/std.h index f505c78b..28a7f6c9 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -371,24 +371,13 @@ class path : public std::filesystem::path { template struct formatter, Char> : nested_formatter, Char> { - private: - // This is a functor because C++11 doesn't support generic lambdas. - struct writer { - const std::bitset& bs; - - template - FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { - for (auto pos = N; pos > 0; --pos) - out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); - return out; - } - }; - public: template auto format(const std::bitset& bs, FormatContext& ctx) const -> decltype(ctx.out()) { - return this->write_padded(ctx, writer{bs}); + auto str = bs.template to_string(); + auto view = basic_string_view(str); + return this->write(ctx, this->nested(view)); } }; diff --git a/test/format-test.cc b/test/format-test.cc index 223fd649..5aee1dac 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -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 : nested_formatter { 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; }