diff --git a/include/fmt/format.h b/include/fmt/format.h index 657924c1..3b73a522 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1338,12 +1338,10 @@ FMT_CONSTEXPR void handle_float_type_spec(Char spec, Handler &&handler) { template FMT_CONSTEXPR void handle_char_specs( - const basic_format_specs &specs, Handler &&handler) { - if (specs.type() && specs.type() != 'c') { - handler.on_int(); - return; - } - if (specs.align() == ALIGN_NUMERIC || specs.flag(~0u) != 0) + 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->flag(~0u) != 0) handler.on_error("invalid format specifier for char"); handler.on_char(); } @@ -1470,7 +1468,7 @@ class arg_formatter_base { private: typedef basic_writer writer_type; writer_type writer_; - format_specs &specs_; + format_specs *specs_; struct char_writer { char_type value; @@ -1479,11 +1477,14 @@ class arg_formatter_base { }; void write_char(char_type value) { - writer_.write_padded(1, specs_, char_writer{value}); + if (specs_) + writer_.write_padded(1, *specs_, char_writer{value}); + else + writer_.write(value); } void write_pointer(const void *p) { - format_specs specs = specs_; + format_specs specs = specs_ ? *specs_ : format_specs(); specs.flags_ = HASH_FLAG; specs.type_ = 'x'; writer_.write_int(reinterpret_cast(p), specs); @@ -1491,22 +1492,24 @@ class arg_formatter_base { protected: writer_type &writer() { return writer_; } - format_specs &spec() { return specs_; } + format_specs *spec() { return specs_; } iterator out() { return writer_.out(); } void write(bool value) { - writer_.write_str(string_view(value ? "true" : "false"), specs_); + string_view sv(value ? "true" : "false"); + specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv); } void write(const char_type *value) { if (!value) FMT_THROW(format_error("string pointer is null")); auto length = std::char_traits::length(value); - writer_.write_str(basic_string_view(value, length), specs_); + basic_string_view sv(value, length); + specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv); } public: - arg_formatter_base(Range r, format_specs &s): writer_(r), specs_(s) {} + arg_formatter_base(Range r, format_specs *s): writer_(r), specs_(s) {} iterator operator()(monostate) { FMT_ASSERT(false, "invalid argument type"); @@ -1519,14 +1522,14 @@ class arg_formatter_base { // MSVC2013 fails to compile separate overloads for bool and char_type so // use std::is_same instead. if (std::is_same::value) { - if (specs_.type_) + if (specs_ && specs_->type_) return (*this)(value ? 1 : 0); write(value != 0); } else if (std::is_same::value) { internal::handle_char_specs( specs_, char_spec_handler(*this, static_cast(value))); } else { - writer_.write_int(value, specs_); + specs_ ? writer_.write_int(value, *specs_) : writer_.write(value); } return out(); } @@ -1534,7 +1537,7 @@ class arg_formatter_base { template typename std::enable_if::value, iterator>::type operator()(T value) { - writer_.write_double(value, specs_); + writer_.write_double(value, specs_ ? *specs_ : format_specs()); return out(); } @@ -1545,7 +1548,12 @@ class arg_formatter_base { char_spec_handler(arg_formatter_base& f, char_type val) : formatter(f), value(val) {} - void on_int() { formatter.writer_.write_int(value, formatter.specs_); } + void on_int() { + if (formatter.specs_) + formatter.writer_.write_int(value, *formatter.specs_); + else + formatter.writer_.write(value); + } void on_char() { formatter.write_char(value); } }; @@ -1561,19 +1569,26 @@ class arg_formatter_base { }; iterator operator()(const char_type *value) { + if (!specs_) return write(value), out(); internal::handle_cstring_type_spec( - specs_.type_, cstring_spec_handler(*this, value)); + specs_->type_, cstring_spec_handler(*this, value)); return out(); } iterator operator()(basic_string_view value) { - internal::check_string_type_spec(specs_.type_, internal::error_handler()); - writer_.write_str(value, specs_); + if (specs_) { + internal::check_string_type_spec( + specs_->type_, internal::error_handler()); + writer_.write_str(value, *specs_); + } else { + writer_.write(value); + } return out(); } iterator operator()(const void *value) { - check_pointer_type_spec(specs_.type_, internal::error_handler()); + if (specs_) + check_pointer_type_spec(specs_->type_, internal::error_handler()); write_pointer(value); return out(); } @@ -2346,7 +2361,7 @@ class arg_formatter: *spec* contains format specifier information for standard argument types. \endrst */ - arg_formatter(context_type &ctx, format_specs &spec) + explicit arg_formatter(context_type &ctx, format_specs *spec = {}) : base(Range(ctx.out()), spec), ctx_(ctx) {} using base::operator(); @@ -2500,7 +2515,7 @@ class basic_writer { auto &&it = reserve((is_negative ? 1 : 0) + num_digits); if (is_negative) *it++ = '-'; - internal::format_decimal(it, abs_value, num_digits); + it = internal::format_decimal(it, abs_value, num_digits); } // The handle_int_type_spec handler that writes an integer. @@ -2775,7 +2790,7 @@ class basic_writer { void write(wstring_view value) { internal::require_wchar(); auto &&it = reserve(value.size()); - it = std::uninitialized_copy(value.begin(), value.end(), it); + it = std::copy(value.begin(), value.end(), it); } template @@ -3176,7 +3191,7 @@ struct formatter< break; case internal::char_type: handle_char_specs( - specs_, + &specs_, internal::char_specs_checker( type_spec, eh)); break; @@ -3211,7 +3226,7 @@ struct formatter< specs_.precision_, specs_.precision_ref, ctx); typedef output_range range_type; - return visit(arg_formatter(ctx, specs_), + return visit(arg_formatter(ctx, &specs_), internal::make_arg(val)); } @@ -3272,7 +3287,7 @@ class dynamic_formatter { checker.end_precision(); typedef output_range range; - visit(arg_formatter(ctx, specs_), + visit(arg_formatter(ctx, &specs_), internal::make_arg(val)); return ctx.out(); } @@ -3328,10 +3343,8 @@ struct format_handler : internal::error_handler { void on_replacement_field(const Char *p) { context.parse_context().advance_to(p); - if (visit(internal::custom_formatter(context), arg)) - return; - basic_format_specs specs; - context.advance_to(visit(ArgFormatter(context, specs), arg)); + if (!visit(internal::custom_formatter(context), arg)) + context.advance_to(visit(ArgFormatter(context), arg)); } iterator on_format_specs(iterator it) { @@ -3347,7 +3360,7 @@ struct format_handler : internal::error_handler { if (*it != '}') on_error("missing '}' in format string"); parse_ctx.advance_to(pointer_from(it)); - context.advance_to(visit(ArgFormatter(context, specs), arg)); + context.advance_to(visit(ArgFormatter(context, &specs), arg)); return it; } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 3ed37b2b..85347690 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -222,12 +222,12 @@ class printf_arg_formatter: context_type &context_; void write_null_pointer(char) { - this->spec().type_ = 0; + this->spec()->type_ = 0; this->write("(nil)"); } void write_null_pointer(wchar_t) { - this->spec().type_ = 0; + this->spec()->type_ = 0; this->write(L"(nil)"); } @@ -243,7 +243,7 @@ class printf_arg_formatter: */ printf_arg_formatter(internal::basic_buffer &buffer, format_specs &spec, context_type &ctx) - : base(back_insert_range>(buffer), spec), + : base(back_insert_range>(buffer), &spec), context_(ctx) {} template @@ -252,13 +252,13 @@ class printf_arg_formatter: // 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_spec = this->spec(); + format_specs &fmt_spec = *this->spec(); if (fmt_spec.type_ != 's') return base::operator()(value ? 1 : 0); fmt_spec.type_ = 0; this->write(value != 0); } else if (std::is_same::value) { - format_specs &fmt_spec = this->spec(); + format_specs &fmt_spec = *this->spec(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') return (*this)(static_cast(value)); fmt_spec.flags_ = 0; @@ -280,7 +280,7 @@ class printf_arg_formatter: iterator operator()(const char *value) { if (value) base::operator()(value); - else if (this->spec().type_ == 'p') + else if (this->spec()->type_ == 'p') write_null_pointer(char_type()); else this->write("(null)"); @@ -291,7 +291,7 @@ class printf_arg_formatter: iterator operator()(const wchar_t *value) { if (value) base::operator()(value); - else if (this->spec().type_ == 'p') + else if (this->spec()->type_ == 'p') write_null_pointer(char_type()); else this->write(L"(null)"); @@ -310,7 +310,7 @@ class printf_arg_formatter: iterator operator()(const void *value) { if (value) return base::operator()(value); - this->spec().type_ = 0; + this->spec()->type_ = 0; write_null_pointer(char_type()); return this->out(); } diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 9969ed9b..88336cbe 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -19,14 +19,15 @@ class custom_arg_formatter : typedef fmt::back_insert_range range; typedef fmt::arg_formatter base; - custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) + custom_arg_formatter( + fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL) : base(ctx, s) {} using base::operator(); iterator operator()(double value) { // Comparing a float to 0.0 is safe - if (round(value * pow(10, spec().precision())) == 0.0) + if (round(value * pow(10, spec()->precision())) == 0.0) value = 0; return base::operator()(value); } diff --git a/test/format-test.cc b/test/format-test.cc index 3cc1b09f..4563c6a9 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1471,7 +1471,7 @@ class mock_arg_formatter: typedef fmt::internal::arg_formatter_base base; typedef buffer_range range; - mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) + mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL) : base(fmt::internal::get_container(ctx.out()), s) { EXPECT_CALL(*this, call(42)); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index cfde1889..50b8e4e8 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -50,7 +50,7 @@ typedef fmt::back_insert_range range; struct test_arg_formatter: fmt::arg_formatter { test_arg_formatter(fmt::format_context &ctx, fmt::format_specs &s) - : fmt::arg_formatter(ctx, s) {} + : fmt::arg_formatter(ctx, &s) {} }; TEST(OStreamTest, CustomArg) {