Skip to content

Commit

Permalink
Simplify default formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Aug 5, 2024
1 parent 15f939c commit 4e0cf20
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 83 deletions.
146 changes: 64 additions & 82 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3640,21 +3640,19 @@ FMT_CONSTEXPR auto write(OutputIt out, const T& value)
// An argument visitor that formats the argument and writes it via the output
// iterator. It's a class and not a generic lambda for compatibility with C++11.
template <typename Char> struct default_arg_formatter {
using iterator = basic_appender<Char>;
using context = buffered_context<Char>;

iterator out;
basic_format_args<context> args;
locale_ref loc;
basic_appender<Char> out;

template <typename T> auto operator()(T value) -> iterator {
return write<Char>(out, value);
}
auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
basic_format_parse_context<Char> parse_ctx({});
context format_ctx(out, args, loc);
void operator()(monostate) { report_error("argument not found"); }

template <typename T> void operator()(T value) { write<Char>(out, value); }

void operator()(typename basic_format_arg<context>::handle h) {
// Use a null locale since the default format must be unlocalized.
auto parse_ctx = basic_format_parse_context<Char>({});
auto format_ctx = context(out, {}, {});
h.format(parse_ctx, format_ctx);
return format_ctx.out();
}
};

Expand Down Expand Up @@ -3690,16 +3688,9 @@ struct dynamic_spec_getter {
}
};

template <typename Context, typename 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 Context>
FMT_CONSTEXPR int get_dynamic_spec(
const arg_ref<typename Context::char_type>& ref, Context& ctx) {
FMT_CONSTEXPR auto get_dynamic_spec(
const arg_ref<typename Context::char_type>& ref, Context& ctx) -> int {
FMT_ASSERT(ref.kind != arg_id_kind::none, "");
auto arg = ref.kind == arg_id_kind::index ? ctx.arg(ref.val.index)
: ctx.arg(ref.val.name);
Expand Down Expand Up @@ -4121,74 +4112,65 @@ FMT_END_EXPORT

namespace detail {

template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
typename vformat_args<Char>::type args, locale_ref loc) {
auto out = basic_appender<Char>(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
auto arg = args.get(0);
if (!arg) report_error("argument not found");
arg.visit(default_arg_formatter<Char>{out, args, loc});
return;
template <typename Char> struct format_handler {
basic_format_parse_context<Char> parse_context;
buffered_context<Char> context;

void on_text(const Char* begin, const Char* end) {
copy_noinline<Char>(begin, end, context.out());
}

struct format_handler {
basic_format_parse_context<Char> parse_context;
buffered_context<Char> context;
FMT_CONSTEXPR auto on_arg_id() -> int { return parse_context.next_arg_id(); }
FMT_CONSTEXPR auto on_arg_id(int id) -> int {
parse_context.check_arg_id(id);
return id;
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
parse_context.check_arg_id(id);
int arg_id = context.arg_id(id);
if (arg_id < 0) report_error("argument not found");
return arg_id;
}

format_handler(basic_appender<Char> p_out, basic_string_view<Char> str,
basic_format_args<buffered_context<Char>> p_args,
locale_ref p_loc)
: parse_context(str), context(p_out, p_args, p_loc) {}
FMT_INLINE void on_replacement_field(int id, const Char*) {
context.arg(id).visit(default_arg_formatter<Char>{context.out()});
}

void on_text(const Char* begin, const Char* end) {
context.advance_to(copy_noinline<Char>(begin, end, context.out()));
auto on_format_specs(int id, const Char* begin, const Char* end)
-> const Char* {
auto arg = context.arg(id);
if (in(arg.type(), set(type::none_type) | set(type::custom_type))) {
if (!arg) report_error("argument not found");
// Not using a visitor for custom types gives better codegen.
arg.format_custom(begin, parse_context, context);
return parse_context.begin();
}

FMT_CONSTEXPR auto on_arg_id() -> int {
return parse_context.next_arg_id();
}
FMT_CONSTEXPR auto on_arg_id(int id) -> int {
parse_context.check_arg_id(id);
return id;
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
parse_context.check_arg_id(id);
int arg_id = context.arg_id(id);
if (arg_id < 0) report_error("argument not found");
return arg_id;
}
auto specs = detail::dynamic_format_specs<Char>();
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
if (specs.width_ref.kind != detail::arg_id_kind::none)
specs.width = detail::get_dynamic_spec(specs.width_ref, context);
if (specs.precision_ref.kind != detail::arg_id_kind::none)
specs.precision = detail::get_dynamic_spec(specs.precision_ref, context);

FMT_INLINE void on_replacement_field(int id, const Char*) {
auto arg = get_arg(context, id);
context.advance_to(arg.visit(default_arg_formatter<Char>{
context.out(), context.args(), context.locale()}));
}
if (begin == end || *begin != '}')
report_error("missing '}' in format string");
arg.visit(arg_formatter<Char>{context.out(), specs, context.locale()});
return begin;
}

auto on_format_specs(int id, const Char* begin, const Char* end)
-> const Char* {
auto arg = get_arg(context, id);
// Not using a visitor for custom types gives better codegen.
if (arg.format_custom(begin, parse_context, context))
return parse_context.begin();
auto specs = detail::dynamic_format_specs<Char>();
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
if (specs.width_ref.kind != detail::arg_id_kind::none)
specs.width = detail::get_dynamic_spec(specs.width_ref, context);
if (specs.precision_ref.kind != detail::arg_id_kind::none) {
specs.precision =
detail::get_dynamic_spec(specs.precision_ref, context);
}
if (begin == end || *begin != '}')
report_error("missing '}' in format string");
context.advance_to(arg.visit(
arg_formatter<Char>{context.out(), specs, context.locale()}));
return begin;
}
FMT_NORETURN void on_error(const char* message) { report_error(message); }
};

FMT_NORETURN void on_error(const char* message) { report_error(message); }
};
detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
typename vformat_args<Char>::type args, locale_ref loc) {
auto out = basic_appender<Char>(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<Char>{out});
parse_format_string<false>(
fmt, format_handler<Char>{basic_format_parse_context<Char>(fmt),
{out, args, loc}});
}

FMT_BEGIN_EXPORT
Expand All @@ -4215,9 +4197,9 @@ FMT_CONSTEXPR FMT_INLINE auto native_formatter<T, Char, TYPE>::format(
specs_.precision_ref.kind == arg_id_kind::none) {
return write<Char>(ctx.out(), val, specs_, ctx.locale());
}
auto specs = specs_;
handle_dynamic_spec(specs.width, specs.width_ref, ctx);
handle_dynamic_spec(specs.precision, specs.precision_ref, ctx);
auto specs = format_specs(specs_);
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
4 changes: 3 additions & 1 deletion include/fmt/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
};

const Char* start = parse_ctx.begin();
Expand Down

0 comments on commit 4e0cf20

Please sign in to comment.