diff --git a/include/fmt/format.h b/include/fmt/format.h index 182859962b4b..9d406d508e1e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4198,6 +4198,38 @@ template struct formatter> : formatter { } }; +template +struct nested_view { + const formatter* fmt; + const T* value; +}; + +template +struct formatter> { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { + return ctx.begin(); + } + auto format(nested_view view, format_context& ctx) const + -> decltype(ctx.out()) { + return view.fmt->format(*view.value, ctx); + } +}; + +template +struct nested_formatter { + private: + formatter formatter_; + + public: + FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { + return formatter_.parse(ctx); + } + + auto nested(const T& value) const -> nested_view { + return nested_view{&formatter_, &value}; + } +}; + // DEPRECATED! join_view will be moved to ranges.h. template struct join_view : detail::view { diff --git a/test/format-test.cc b/test/format-test.cc index dd3881716c95..aef2d697b441 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1775,6 +1775,23 @@ TEST(format_test, group_digits_view) { EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000"); } +struct point { + double x, y; +}; + +FMT_BEGIN_NAMESPACE +template <> +struct formatter : nested_formatter { + auto format(point p, format_context& ctx) const -> decltype(ctx.out()) { + return format_to(ctx.out(), "({}, {})", nested(p.x), nested(p.y)); + } +}; +FMT_END_NAMESPACE + +TEST(format_test, nested_formatter) { + EXPECT_EQ(fmt::format("{:.2f}", point{1, 2}), "(1.00, 2.00)"); +} + enum test_enum { foo, bar }; auto format_as(test_enum e) -> int { return e; }