From 9cb347b4b2e80fc9fbf57b8621746663c3f870f6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 21 Mar 2021 07:28:46 -0700 Subject: [PATCH] Simplify argument formatters --- include/fmt/compile.h | 11 +++--- include/fmt/format.h | 71 +++++++++++++--------------------- include/fmt/locale.h | 4 +- include/fmt/printf.h | 89 +++++++++++++------------------------------ 4 files changed, 61 insertions(+), 114 deletions(-) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index fc419266..580cacde 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -307,7 +307,9 @@ void format_arg( visit_format_arg(custom_formatter(parse_ctx, ctx), arg); } else { ctx.advance_to(visit_format_arg( - arg_formatter(ctx), arg)); + default_arg_formatter{ + ctx.out(), ctx.args(), ctx.locale()}, + arg)); } } @@ -368,10 +370,9 @@ auto vformat_to(OutputIt out, CompiledFormat& cf, if (specs.precision >= 0) checker.check_precision(); advance_to(parse_ctx, part.arg_id_end); - ctx.advance_to( - visit_format_arg(arg_formatter( - ctx, &specs), - arg)); + ctx.advance_to(visit_format_arg( + arg_formatter(ctx, specs), + arg)); break; } } diff --git a/include/fmt/format.h b/include/fmt/format.h index 0215a0a7..5289e2e5 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1485,11 +1485,10 @@ FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { } template -FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs& specs, Handler&& handler) { - if (!specs) return handler.on_char(); - if (specs->type && specs->type != 'c') return handler.on_int(); - if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + if (specs.type && specs.type != 'c') return handler.on_int(); + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) handler.on_error("invalid format specifier for char"); handler.on_char(); } @@ -2203,8 +2202,8 @@ class arg_formatter_base { private: iterator out_; + const format_specs& specs_; locale_ref locale_; - format_specs* specs_; // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to out_. @@ -2249,7 +2248,7 @@ class arg_formatter_base { } void write_pointer(const void* p) { - out_ = write_ptr(out_, to_uintptr(p), specs_); + out_ = write_ptr(out_, to_uintptr(p), &specs_); } struct char_spec_handler : ErrorHandler { @@ -2263,13 +2262,10 @@ class arg_formatter_base { // char is only formatted as int if there are specs. formatter.out_ = detail::write_int(formatter.out_, static_cast(value), - *formatter.specs_, formatter.locale_); + formatter.specs_, formatter.locale_); } FMT_CONSTEXPR void on_char() { - if (formatter.specs_) - formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); - else - formatter.write(value); + formatter.out_ = write_char(formatter.out_, value, formatter.specs_); } }; @@ -2286,28 +2282,22 @@ class arg_formatter_base { protected: iterator out() { return out_; } - format_specs* specs() { return specs_; } + const format_specs& specs() { return specs_; } FMT_CONSTEXPR void write(bool value) { - if (specs_) - write(string_view(value ? "true" : "false"), *specs_); - else - out_ = detail::write(out_, value); + write(string_view(value ? "true" : "false"), specs_); } void write(const Char* value) { - if (!value) { + if (value) + write(basic_string_view(value), specs_); + else FMT_THROW(format_error("string pointer is null")); - } else { - auto length = std::char_traits::length(value); - basic_string_view sv(value, length); - specs_ ? write(sv, *specs_) : write(sv); - } } public: - constexpr arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) - : out_(out), locale_(loc), specs_(s) {} + constexpr arg_formatter_base(OutputIt out, const format_specs& s, locale_ref loc) + : out_(out), specs_(s), locale_(loc) {} iterator operator()(monostate) { FMT_ASSERT(false, "invalid argument type"); @@ -2316,8 +2306,7 @@ class arg_formatter_base { template ::value)> FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) { - return out_ = specs_ ? detail::write_int(out_, value, *specs_, locale_) - : detail::write(out_, value); + return out_ = detail::write_int(out_, value, specs_, locale_); } FMT_CONSTEXPR iterator operator()(Char value) { @@ -2327,40 +2316,33 @@ class arg_formatter_base { } FMT_CONSTEXPR iterator operator()(bool value) { - if (specs_ && specs_->type && specs_->type != 's') - return (*this)(value ? 1 : 0); + if (specs_.type && specs_.type != 's') return (*this)(value ? 1 : 0); write(value != 0); return out_; } template ::value)> iterator operator()(T value) { - auto specs = specs_ ? *specs_ : format_specs(); if (const_check(is_supported_floating_point(value))) - out_ = detail::write(out_, value, specs, locale_); + out_ = detail::write(out_, value, specs_, locale_); else FMT_ASSERT(false, "unsupported float argument type"); return out_; } iterator operator()(const Char* value) { - if (!specs_) return write(value), out_; - handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); + handle_cstring_type_spec(specs_.type, cstring_spec_handler(*this, value)); return out_; } FMT_CONSTEXPR iterator operator()(basic_string_view value) { - if (specs_) { - check_string_type_spec(specs_->type, error_handler()); - write(value, *specs_); - } else { - write(value); - } + check_string_type_spec(specs_.type, error_handler()); + write(value, specs_); return out_; } iterator operator()(const void* value) { - if (specs_) check_pointer_type_spec(specs_->type, error_handler()); + check_pointer_type_spec(specs_.type, error_handler()); write_pointer(value); return out_; } @@ -2387,8 +2369,7 @@ class arg_formatter : public arg_formatter_base { *specs* contains format specifier information for standard argument types. \endrst */ - constexpr explicit arg_formatter(context_type& ctx, - format_specs* specs = nullptr) + constexpr explicit arg_formatter(context_type& ctx, const format_specs& specs) : base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {} using base::operator(); @@ -3167,7 +3148,7 @@ struct format_handler : detail::error_handler { begin = parse_format_specs(begin, end, handler); if (begin == end || *begin != '}') on_error("missing '}' in format string"); context.advance_to( - visit_format_arg(arg_formatter(context, &specs), arg)); + visit_format_arg(arg_formatter(context, specs), arg)); return begin; } }; @@ -3496,7 +3477,7 @@ struct formatter(specs_.type, eh)); + specs_, detail::char_specs_checker(specs_.type, eh)); break; case detail::type::float_type: if (detail::const_check(FMT_USE_FLOAT)) @@ -3544,7 +3525,7 @@ struct formatter; - return visit_format_arg(af(ctx, &specs), + return visit_format_arg(af(ctx, specs), detail::make_arg(val)); } @@ -3647,7 +3628,7 @@ template class dynamic_formatter { if (specs_.precision >= 0) checker.end_precision(); using af = detail::arg_formatter; - visit_format_arg(af(ctx, &specs_), detail::make_arg(val)); + visit_format_arg(af(ctx, specs_), detail::make_arg(val)); return ctx.out(); } diff --git a/include/fmt/locale.h b/include/fmt/locale.h index 7301bf92..b5990ef1 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -52,8 +52,8 @@ inline OutputIt vformat_to( template >::value> -inline auto format_to(OutputIt out, const std::locale& loc, - const S& format_str, Args&&... args) -> +inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str, + Args&&... args) -> typename std::enable_if::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to(out, loc, to_string_view(format_str), vargs); diff --git a/include/fmt/printf.h b/include/fmt/printf.h index b95b56cf..691fc0ff 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -207,50 +207,32 @@ template class basic_printf_context; */ template class printf_arg_formatter : public detail::arg_formatter_base { - public: - using iterator = OutputIt; - private: - using char_type = Char; using base = detail::arg_formatter_base; using context_type = basic_printf_context; + using format_specs = typename base::format_specs; context_type& context_; - void write_null_pointer(char) { - this->specs()->type = 0; - this->write("(nil)"); - } - - void write_null_pointer(wchar_t) { - this->specs()->type = 0; - this->write(L"(nil)"); + OutputIt write_null_pointer(bool is_string = false) { + auto s = this->specs(); + s.type = 0; + return detail::write(this->out(), + string_view(is_string ? "(null)" : "(nil)"), s); } public: - using format_specs = typename base::format_specs; + printf_arg_formatter(OutputIt iter, format_specs& specs, context_type& ctx) + : base(iter, specs, detail::locale_ref()), context_(ctx) {} - /** - \rst - Constructs an argument formatter object. - *buffer* is a reference to the output buffer and *specs* contains format - specifier information for standard argument types. - \endrst - */ - printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) - : base(iter, &specs, detail::locale_ref()), context_(ctx) {} + OutputIt operator()(monostate value) { return base::operator()(value); } template ::value)> - iterator operator()(T value) { - // MSVC2013 fails to compile separate overloads for bool and char_type so - // use std::is_same instead. - if (std::is_same::value) { - format_specs& fmt_specs = *this->specs(); - if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0); - fmt_specs.type = 0; - this->write(value != 0); - } else if (std::is_same::value) { - format_specs& fmt_specs = *this->specs(); + OutputIt operator()(T value) { + // MSVC2013 fails to compile separate overloads for bool and Char so use + // std::is_same instead. + if (std::is_same::value) { + format_specs fmt_specs = this->specs(); if (fmt_specs.type && fmt_specs.type != 'c') return (*this)(static_cast(value)); fmt_specs.sign = sign::none; @@ -260,56 +242,39 @@ class printf_arg_formatter : public detail::arg_formatter_base { // ignored for non-numeric types if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) fmt_specs.align = align::right; - return base::operator()(value); - } else { - return base::operator()(value); + return write_char(this->out(), static_cast(value), fmt_specs); } - return this->out(); + return base::operator()(value); } template ::value)> - iterator operator()(T value) { + OutputIt operator()(T value) { return base::operator()(value); } /** Formats a null-terminated C string. */ - iterator operator()(const char* value) { - if (value) - base::operator()(value); - else if (this->specs()->type == 'p') - write_null_pointer(char_type()); - else - this->write("(null)"); - return this->out(); + OutputIt operator()(const char* value) { + if (value) return base::operator()(value); + return write_null_pointer(this->specs().type != 'p'); } /** Formats a null-terminated wide C string. */ - iterator operator()(const wchar_t* value) { - if (value) - base::operator()(value); - else if (this->specs()->type == 'p') - write_null_pointer(char_type()); - else - this->write(L"(null)"); - return this->out(); + OutputIt operator()(const wchar_t* value) { + if (value) return base::operator()(value); + return write_null_pointer(this->specs().type != 'p'); } - iterator operator()(basic_string_view value) { + OutputIt operator()(basic_string_view value) { return base::operator()(value); } - iterator operator()(monostate value) { return base::operator()(value); } - /** Formats a pointer. */ - iterator operator()(const void* value) { - if (value) return base::operator()(value); - this->specs()->type = 0; - write_null_pointer(char_type()); - return this->out(); + OutputIt operator()(const void* value) { + return value ? base::operator()(value) : write_null_pointer(); } /** Formats an argument of a custom (user-defined) type. */ - iterator operator()(typename basic_format_arg::handle handle) { + OutputIt operator()(typename basic_format_arg::handle handle) { handle.format(context_.parse_context(), context_); return this->out(); }