Skip to content

Commit

Permalink
Simplify handling of dynamic specs
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Aug 4, 2024
1 parent 58aba5a commit 7891699
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 59 deletions.
9 changes: 3 additions & 6 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -2283,10 +2283,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref_, ctx);
detail::handle_dynamic_spec(specs.width, width_ref_, ctx);
detail::handle_dynamic_spec(precision, precision_ref_, ctx);
if (begin == end || *begin == '}') {
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
Expand Down Expand Up @@ -2392,8 +2390,7 @@ template <typename Char> struct formatter<std::tm, Char> {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec(specs.width, width_ref_, ctx);

auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
Expand Down
58 changes: 19 additions & 39 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3677,60 +3677,46 @@ template <typename Char> struct arg_formatter {
}
};

struct width_checker {
struct dynamic_spec_getter {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
if (is_negative(value)) report_error("negative width");
if (is_negative(value)) report_error("negative width/precision");
return static_cast<unsigned long long>(value);
}

template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
report_error("width is not integer");
report_error("width/precision is not integer");
return 0;
}
};

struct precision_checker {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
if (is_negative(value)) report_error("negative precision");
return static_cast<unsigned long long>(value);
}

template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
report_error("precision is not integer");
return 0;
}
};

template <typename Handler, typename FormatArg>
FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int {
unsigned long long value = arg.visit(Handler());
template <typename Context>
FMT_CONSTEXPR auto get_dynamic_spec(basic_format_arg<Context> arg) -> int {
unsigned long long value = arg.visit(dynamic_spec_getter());
if (value > to_unsigned(max_value<int>())) report_error("number is too big");
return static_cast<int>(value);
}

template <typename Context, typename ID>
FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg<Context> {
auto arg = ctx.arg(id);
if (!arg) report_error("argument not found");
return arg;
}

template <typename Handler, typename Context>
template <typename Context>
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
arg_ref<typename Context::char_type> ref,
Context& ctx) {
switch (ref.kind) {
case arg_id_kind::none:
break;
case arg_id_kind::index:
value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index));
value = get_dynamic_spec(get_arg(ctx, ref.val.index));
break;
case arg_id_kind::name:
value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name));
value = get_dynamic_spec(get_arg(ctx, ref.val.name));
break;
}
}
Expand Down Expand Up @@ -3987,10 +3973,8 @@ template <> struct formatter<bytes> {
template <typename FormatContext>
auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx);
return detail::write_bytes<char>(ctx.out(), b.data_, specs);
}
};
Expand Down Expand Up @@ -4028,10 +4012,8 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
auto format(group_digits_view<T> t, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx);
auto arg = detail::make_write_int_arg(t.value, specs.sign);
return detail::write_int(
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(arg.abs_value),
Expand Down Expand Up @@ -4196,10 +4178,9 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
return parse_context.begin();
auto specs = detail::dynamic_format_specs<Char>();
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
detail::handle_dynamic_spec<detail::width_checker>(
specs.width, specs.width_ref, context);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, context);
detail::handle_dynamic_spec(specs.width, specs.width_ref, context);
detail::handle_dynamic_spec(specs.precision, specs.precision_ref,
context);
if (begin == end || *begin != '}')
report_error("missing '}' in format string");
context.advance_to(arg.visit(
Expand Down Expand Up @@ -4237,9 +4218,8 @@ FMT_CONSTEXPR FMT_INLINE auto native_formatter<T, Char, TYPE>::format(
return write<Char>(ctx.out(), val, specs_, ctx.locale());
}
auto specs = specs_;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision, specs.precision_ref,
ctx);
handle_dynamic_spec(specs.width, specs.width_ref, ctx);
handle_dynamic_spec(specs.precision, specs.precision_ref, ctx);
return write<Char>(ctx.out(), val, specs, ctx.locale());
}

Expand Down
9 changes: 3 additions & 6 deletions include/fmt/std.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();

detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec(specs.width, width_ref_, ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
Expand Down Expand Up @@ -672,10 +671,8 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx);
}

if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
Expand Down
16 changes: 8 additions & 8 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -897,11 +897,11 @@ TEST(format_test, runtime_width) {
"invalid format string");

EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
"negative width");
"negative width/precision");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
"negative width");
"negative width/precision");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
Expand All @@ -911,9 +911,9 @@ TEST(format_test, runtime_width) {
format_error, "number is too big");

EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
"width is not integer");
"width/precision is not integer");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
"width is not integer");
"width/precision is not integer");

EXPECT_EQ(fmt::format("{0:{1}}", -42, 4), " -42");
EXPECT_EQ(fmt::format("{0:{1}}", 42u, 5), " 42");
Expand Down Expand Up @@ -1118,11 +1118,11 @@ TEST(format_test, runtime_precision) {
"invalid format string");

EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1),
format_error, "negative precision");
format_error, "negative width/precision");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1u)),
format_error, "number is too big");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1l),
format_error, "negative precision");
format_error, "negative width/precision");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (value + 1)),
Expand All @@ -1132,9 +1132,9 @@ TEST(format_test, runtime_precision) {
format_error, "number is too big");

EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, '0'),
format_error, "precision is not integer");
format_error, "width/precision is not integer");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, 0.0),
format_error, "precision is not integer");
format_error, "width/precision is not integer");

EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
"invalid format specifier");
Expand Down

0 comments on commit 7891699

Please sign in to comment.