Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3885,6 +3885,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_; }

Expand Down Expand Up @@ -4085,20 +4088,31 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
}
};

template <typename T, typename Char> struct nested_view {
template <typename T, typename Char, typename OuterContext = context>
struct nested_view {
const formatter<T, Char>* fmt;
const T* value;
// Args and locale of the outer format call so that dynamic width/precision
// arg indices are resolved against the user's original argument list rather
// than the arguments of the intermediate format_to invocation.
basic_format_args<OuterContext> outer_args;
locale_ref outer_loc;
};

template <typename T, typename Char>
struct formatter<nested_view<T, Char>, Char> {
template <typename T, typename Char, typename OuterContext>
struct formatter<nested_view<T, Char, OuterContext>, 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
auto format(nested_view<T, Char, OuterContext> view, FormatContext& ctx) const
-> decltype(ctx.out()) {
return view.fmt->format(*view.value, ctx);
// Delegate to the inner formatter using a context that carries the outer
// format args so dynamic specs like "{:.{}}" can be resolved.
auto outer_ctx = OuterContext(ctx.out(), view.outer_args, view.outer_loc);
auto it = view.fmt->format(*view.value, outer_ctx);
ctx.advance_to(it);
Comment on lines +4112 to +4114
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work if iterators are compatible (same context types) so there shouldn't be parameterization on OuterContext.

return ctx.out();
}
};

Expand Down Expand Up @@ -4141,7 +4155,14 @@ template <typename T, typename Char = char> struct nested_formatter {
}

auto nested(const T& value) const -> nested_view<T, Char> {
return nested_view<T, Char>{&formatter_, &value};
return nested_view<T, Char>{&formatter_, &value, {}, {}};
}

template <typename FormatContext>
auto nested(const T& value, FormatContext& ctx) const
-> nested_view<T, Char, FormatContext> {
return nested_view<T, Char, FormatContext>{
&formatter_, &value, ctx.args(), ctx.locale()};
}
};

Expand Down
12 changes: 9 additions & 3 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1984,9 +1984,9 @@ 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_padded(ctx, [this, p, &ctx](auto out) -> decltype(out) {
return fmt::format_to(out, "({}, {})", this->nested(p.x, ctx),
this->nested(p.y, ctx));
});
}
};
Expand All @@ -1995,6 +1995,12 @@ FMT_END_NAMESPACE
TEST(format_test, nested_formatter) {
EXPECT_EQ(fmt::format("{:>16.2f}", point{1, 2}), " (1.00, 2.00)");
}

TEST(format_test, nested_formatter_dynamic_precision) {
// https://github.com/fmtlib/fmt/issues/3860
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, let's remove.

EXPECT_EQ(fmt::format("{:>16.{}f}", point{1, 2}, 2), " (1.00, 2.00)");
EXPECT_EQ(fmt::format("{:>16.{}f}", point{1, 2}, 3), " (1.000, 2.000)");
}
#endif // __cpp_generic_lambdas

enum test_enum { foo, bar };
Expand Down
Loading