diff --git a/include/fmt/format.h b/include/fmt/format.h index f10018be..f6b37f4d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1069,7 +1069,7 @@ FMT_CONSTEXPR typename internal::result_of::type typedef typename Context::char_type char_type; switch (arg.type_) { case internal::none_type: - return vis(monostate()); + break; case internal::name_arg_type: FMT_ASSERT(false, "invalid argument type"); break; @@ -1099,7 +1099,7 @@ FMT_CONSTEXPR typename internal::result_of::type case internal::custom_type: return vis(typename basic_arg::handle(arg.value_.custom)); } - return typename internal::result_of::type(); + return vis(monostate()); } enum alignment { @@ -1392,6 +1392,7 @@ template class arg_formatter_base { public: typedef typename Range::value_type char_type; + typedef decltype(internal::declval().begin()) iterator; typedef basic_format_specs format_specs; private: @@ -1422,6 +1423,7 @@ class arg_formatter_base { protected: writer_type &writer() { return writer_; } format_specs &spec() { return specs_; } + iterator out() { return writer_.out(); } void write(bool value) { writer_.write_str(string_view(value ? "true" : "false"), specs_); @@ -1437,22 +1439,30 @@ class arg_formatter_base { public: arg_formatter_base(Range r, format_specs &s): writer_(r), specs_(s) {} - void operator()(monostate) { + iterator operator()(monostate) { FMT_ASSERT(false, "invalid argument type"); + return out(); } template - typename std::enable_if::value>::type - operator()(T value) { writer_.write_int(value, specs_); } + typename std::enable_if::value, iterator>::type + operator()(T value) { + writer_.write_int(value, specs_); + return out(); + } template - typename std::enable_if::value>::type - operator()(T value) { writer_.write_double(value, specs_); } + typename std::enable_if::value, iterator>::type + operator()(T value) { + writer_.write_double(value, specs_); + return out(); + } - void operator()(bool value) { + iterator operator()(bool value) { if (specs_.type_) return (*this)(value ? 1 : 0); write(value); + return out(); } struct char_spec_handler : internal::error_handler { @@ -1466,8 +1476,9 @@ class arg_formatter_base { void on_char() { formatter.write_char(value); } }; - void operator()(char_type value) { + iterator operator()(char_type value) { internal::handle_char_specs(specs_, char_spec_handler(*this, value)); + return out(); } struct cstring_spec_handler : internal::error_handler { @@ -1481,19 +1492,22 @@ class arg_formatter_base { void on_pointer() { formatter.write_pointer(value); } }; - void operator()(const char_type *value) { + iterator operator()(const char_type *value) { internal::handle_cstring_type_spec( specs_.type_, cstring_spec_handler(*this, value)); + return out(); } - void operator()(basic_string_view value) { + iterator operator()(basic_string_view value) { internal::check_string_type_spec(specs_.type_, internal::error_handler()); writer_.write_str(value, specs_); + return out(); } - void operator()(const void *value) { + iterator operator()(const void *value) { check_pointer_type_spec(specs_.type_, internal::error_handler()); write_pointer(value); + return out(); } }; @@ -2172,14 +2186,14 @@ class arg_formatter: public internal::function, public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; - typedef decltype(internal::declval().begin()) iterator; typedef internal::arg_formatter_base base; - typedef basic_context context_type; + typedef basic_context context_type; context_type &ctx_; public: typedef Range range; + typedef typename base::iterator iterator; typedef typename base::format_specs format_specs; /** @@ -2195,8 +2209,9 @@ class arg_formatter: using base::operator(); /** Formats an argument of a user-defined type. */ - void operator()(typename basic_arg::handle handle) const { + iterator operator()(typename basic_arg::handle handle) { handle.format(ctx_); + return this->out(); } }; @@ -2290,6 +2305,8 @@ class basic_writer { static char_type *get(char_type *p) { return p; } #endif + iterator out() const { return out_; } + // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to out_. auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) { @@ -2668,7 +2685,6 @@ void basic_writer::write_padded( } } - template template void basic_writer::write_str( @@ -3213,18 +3229,16 @@ struct format_handler : internal::error_handler { void on_replacement_field(iterator it) { context.parse_context().advance_to(pointer_from(it)); - using internal::custom_formatter; - if (visit(custom_formatter(context), arg)) + if (visit(internal::custom_formatter(context), arg)) return; basic_format_specs specs; - visit(ArgFormatter(context, specs), arg); + context.advance_to(visit(ArgFormatter(context, specs), arg)); } iterator on_format_specs(iterator it) { auto& parse_ctx = context.parse_context(); parse_ctx.advance_to(pointer_from(it)); - using internal::custom_formatter; - if (visit(custom_formatter(context), arg)) + if (visit(internal::custom_formatter(context), arg)) return iterator(parse_ctx); basic_format_specs specs; using internal::specs_handler; @@ -3234,7 +3248,7 @@ struct format_handler : internal::error_handler { if (*it != '}') on_error("missing '}' in format string"); parse_ctx.advance_to(pointer_from(it)); - 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 15bdf7b9..43d700dc 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -246,45 +246,49 @@ class printf_arg_formatter: using base::operator(); /** Formats an argument of type ``bool``. */ - void operator()(bool value) { + iterator operator()(bool value) { format_specs &fmt_spec = this->spec(); if (fmt_spec.type_ != 's') return (*this)(value ? 1 : 0); fmt_spec.type_ = 0; this->write(value); + return this->out(); } /** Formats a character. */ - void operator()(char_type value) { + iterator operator()(char_type value) { format_specs &fmt_spec = this->spec(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') return (*this)(static_cast(value)); fmt_spec.flags_ = 0; fmt_spec.align_ = ALIGN_RIGHT; - base::operator()(value); + return base::operator()(value); } /** Formats a null-terminated C string. */ - void operator()(const char *value) { + iterator operator()(const char *value) { if (value) base::operator()(value); else if (this->spec().type_ == 'p') write_null_pointer(); else this->write("(null)"); + return this->out(); } /** Formats a pointer. */ - void operator()(const void *value) { + iterator operator()(const void *value) { if (value) return base::operator()(value); this->spec().type_ = 0; write_null_pointer(); + return this->out(); } /** Formats an argument of a custom (user-defined) type. */ - void operator()(typename basic_arg::handle handle) { + iterator operator()(typename basic_arg::handle handle) { handle.format(context_); + return this->out(); } }; diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index b7a85a5a..1a0e9a37 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -21,10 +21,10 @@ class custom_arg_formatter : using base::operator(); - void operator()(double value) { + iterator operator()(double value) { if (round(value * pow(10, spec().precision())) == 0) value = 0; - base::operator()(value); + return base::operator()(value); } }; diff --git a/test/format-test.cc b/test/format-test.cc index cb1cde27..b4a53c7a 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1416,9 +1416,14 @@ class mock_arg_formatter: using base::operator(); - void operator()(int value) { call(value); } + iterator operator()(int value) { + call(value); + return base::operator()(value); + } - void operator()(fmt::basic_arg::handle) {} + iterator operator()(fmt::basic_arg::handle) { + return base::operator()(fmt::monostate()); + } }; void custom_vformat(fmt::string_view format_str, fmt::format_args args) { @@ -1510,7 +1515,12 @@ TEST(FormatTest, FormatToN) { buffer[3] = 'x'; auto result = fmt::format_to_n(buffer, 3, "{}", 12345); EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); EXPECT_EQ("123x", fmt::string_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, "{:s}", "foobar"); + EXPECT_EQ(6u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("foox", fmt::string_view(buffer, 4)); } #if FMT_USE_CONSTEXPR