mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-28 18:32:46 +00:00
Implement nested formatter
This commit is contained in:
parent
f6ca4ea199
commit
0e01e46c11
@ -2302,9 +2302,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
dynamic_format_specs<Char>& specs;
|
dynamic_format_specs<Char>& specs;
|
||||||
type arg_type;
|
type arg_type;
|
||||||
|
|
||||||
FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
|
FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
|
||||||
if (!in(arg_type, set)) throw_format_error("invalid format specifier");
|
if (!in(arg_type, set)) {
|
||||||
specs.type = type;
|
if (arg_type == type::none_type) return begin;
|
||||||
|
throw_format_error("invalid format specifier");
|
||||||
|
}
|
||||||
|
specs.type = pres_type;
|
||||||
return begin + 1;
|
return begin + 1;
|
||||||
}
|
}
|
||||||
} parse_presentation_type{begin, specs, arg_type};
|
} parse_presentation_type{begin, specs, arg_type};
|
||||||
@ -2321,6 +2324,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
case '+':
|
case '+':
|
||||||
case '-':
|
case '-':
|
||||||
case ' ':
|
case ' ':
|
||||||
|
if (arg_type == type::none_type) return begin;
|
||||||
enter_state(state::sign, in(arg_type, sint_set | float_set));
|
enter_state(state::sign, in(arg_type, sint_set | float_set));
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '+':
|
case '+':
|
||||||
@ -2336,14 +2340,17 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
++begin;
|
++begin;
|
||||||
break;
|
break;
|
||||||
case '#':
|
case '#':
|
||||||
|
if (arg_type == type::none_type) return begin;
|
||||||
enter_state(state::hash, is_arithmetic_type(arg_type));
|
enter_state(state::hash, is_arithmetic_type(arg_type));
|
||||||
specs.alt = true;
|
specs.alt = true;
|
||||||
++begin;
|
++begin;
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
enter_state(state::zero);
|
enter_state(state::zero);
|
||||||
if (!is_arithmetic_type(arg_type))
|
if (!is_arithmetic_type(arg_type)) {
|
||||||
|
if (arg_type == type::none_type) return begin;
|
||||||
throw_format_error("format specifier requires numeric argument");
|
throw_format_error("format specifier requires numeric argument");
|
||||||
|
}
|
||||||
if (specs.align == align::none) {
|
if (specs.align == align::none) {
|
||||||
// Ignore 0 if align is specified for compatibility with std::format.
|
// Ignore 0 if align is specified for compatibility with std::format.
|
||||||
specs.align = align::numeric;
|
specs.align = align::numeric;
|
||||||
@ -2365,12 +2372,14 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
|||||||
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
|
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
|
||||||
break;
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
|
if (arg_type == type::none_type) return begin;
|
||||||
enter_state(state::precision,
|
enter_state(state::precision,
|
||||||
in(arg_type, float_set | string_set | cstring_set));
|
in(arg_type, float_set | string_set | cstring_set));
|
||||||
begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
|
begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
|
||||||
ctx);
|
ctx);
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
|
if (arg_type == type::none_type) return begin;
|
||||||
enter_state(state::locale, is_arithmetic_type(arg_type));
|
enter_state(state::locale, is_arithmetic_type(arg_type));
|
||||||
specs.localized = true;
|
specs.localized = true;
|
||||||
++begin;
|
++begin;
|
||||||
|
@ -4222,13 +4222,35 @@ struct formatter<nested_view<T>> {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct nested_formatter {
|
struct nested_formatter {
|
||||||
private:
|
private:
|
||||||
|
int width_;
|
||||||
|
detail::fill_t<char> fill_;
|
||||||
|
align_t align_ : 4;
|
||||||
formatter<T> formatter_;
|
formatter<T> formatter_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* {
|
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* {
|
||||||
|
auto specs = detail::dynamic_format_specs<char>();
|
||||||
|
auto it = parse_format_specs(
|
||||||
|
ctx.begin(), ctx.end(), specs, ctx, detail::type::none_type);
|
||||||
|
width_ = specs.width;
|
||||||
|
fill_ = specs.fill;
|
||||||
|
align_ = specs.align;
|
||||||
|
ctx.advance_to(it);
|
||||||
return formatter_.parse(ctx);
|
return formatter_.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
auto write_padded(format_context& ctx, F write) const -> decltype(ctx.out()) {
|
||||||
|
if (width_ == 0) return write(ctx.out());
|
||||||
|
auto buf = memory_buffer();
|
||||||
|
write(std::back_inserter(buf));
|
||||||
|
auto specs = format_specs<>();
|
||||||
|
specs.width = width_;
|
||||||
|
specs.fill = fill_;
|
||||||
|
specs.align = align_;
|
||||||
|
return detail::write(ctx.out(), string_view(buf.data(), buf.size()), specs);
|
||||||
|
}
|
||||||
|
|
||||||
auto nested(const T& value) const -> nested_view<T> {
|
auto nested(const T& value) const -> nested_view<T> {
|
||||||
return nested_view<T>{&formatter_, &value};
|
return nested_view<T>{&formatter_, &value};
|
||||||
}
|
}
|
||||||
|
@ -1779,22 +1779,25 @@ TEST(format_test, group_digits_view) {
|
|||||||
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
|
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_generic_lambdas
|
||||||
struct point {
|
struct point {
|
||||||
double x, y;
|
double x, y;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <>
|
template <> struct formatter<point> : nested_formatter<double> {
|
||||||
struct formatter<point> : nested_formatter<double> {
|
|
||||||
auto format(point p, format_context& ctx) const -> decltype(ctx.out()) {
|
auto format(point p, format_context& ctx) const -> decltype(ctx.out()) {
|
||||||
return format_to(ctx.out(), "({}, {})", nested(p.x), nested(p.y));
|
return write_padded(ctx, [this, p](auto out) -> decltype(out) {
|
||||||
|
return format_to(out, "({}, {})", nested(p.x), nested(p.y));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(format_test, nested_formatter) {
|
TEST(format_test, nested_formatter) {
|
||||||
EXPECT_EQ(fmt::format("{:.2f}", point{1, 2}), "(1.00, 2.00)");
|
EXPECT_EQ(fmt::format("{:>16.2f}", point{1, 2}), " (1.00, 2.00)");
|
||||||
}
|
}
|
||||||
|
#endif // __cpp_generic_lambdas
|
||||||
|
|
||||||
enum test_enum { foo, bar };
|
enum test_enum { foo, bar };
|
||||||
auto format_as(test_enum e) -> int { return e; }
|
auto format_as(test_enum e) -> int { return e; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user