diff --git a/include/fmt/compile.h b/include/fmt/compile.h index d7e6449e..2a07d2f6 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -608,8 +608,7 @@ template format(const CompiledFormat& cf, const Args&... args) { basic_memory_buffer buffer; using context = buffer_context; - detail::buffer& base = buffer; - detail::cf::vformat_to(std::back_inserter(base), cf, + detail::cf::vformat_to(detail::buffer_appender(buffer), cf, make_format_args(args...)); return to_string(buffer); } diff --git a/include/fmt/core.h b/include/fmt/core.h index 761162eb..f71e00b6 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -722,6 +722,17 @@ class container_buffer : public buffer { : buffer(c.size()), container_(c) {} }; +// An output iterator that appends to the buffer. +// It is used to reduce symbol sizes for the common case. +template +class buffer_appender : public std::back_insert_iterator> { + public: + explicit buffer_appender(buffer& buf) + : std::back_insert_iterator>(buf) {} + buffer_appender(std::back_insert_iterator> it) + : std::back_insert_iterator>(it) {} +}; + // Extracts a reference to the container from back_insert_iterator. template inline Container& get_container(std::back_insert_iterator it) { @@ -1356,13 +1367,13 @@ template class basic_format_context { template using buffer_context = - basic_format_context>, Char>; + basic_format_context, Char>; using format_context = buffer_context; using wformat_context = buffer_context; -// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164. +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. #define FMT_BUFFER_CONTEXT(Char) \ - basic_format_context>, Char> + basic_format_context, Char> /** \rst @@ -1772,7 +1783,7 @@ std::basic_string vformat( FMT_API std::string vformat(string_view format_str, format_args args); template -typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to( +buffer_appender vformat_to( buffer& buf, basic_string_view format_str, basic_format_args)> args); diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index d8c9c8a5..a0423c7f 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1296,6 +1296,19 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) { return next; } + +struct stringifier { + template FMT_INLINE std::string operator()(T value) const { + return to_string(value); + } + std::string operator()(basic_format_arg::handle h) const { + memory_buffer buf; + format_parse_context parse_ctx({}); + format_context format_ctx(buffer_appender(buf), {}, {}); + h.format(parse_ctx, format_ctx); + return to_string(buf); + } +}; } // namespace detail template <> struct formatter { @@ -1384,20 +1397,6 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -struct stringifier { - template FMT_INLINE std::string operator()(T value) const { - return to_string(value); - } - std::string operator()(basic_format_arg::handle h) const { - memory_buffer buf; - detail::buffer& base = buf; - format_parse_context parse_ctx({}); - format_context format_ctx(std::back_inserter(base), {}, {}); - h.format(parse_ctx, format_ctx); - return to_string(buf); - } -}; - FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { auto arg = args.get(0); diff --git a/include/fmt/format.h b/include/fmt/format.h index ddf086f8..4e7f6aba 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -371,6 +371,11 @@ reserve(std::back_insert_iterator it, size_t n) { return make_checked(get_data(c) + size, n); } +template +inline checked_ptr reserve(buffer_appender it, size_t n) { + return reserve(std::back_insert_iterator>(it), n); +} + template inline Iterator& reserve(Iterator& it, size_t) { return it; } @@ -2933,7 +2938,7 @@ class arg_formatter : public arg_formatter_base { template using arg_formatter FMT_DEPRECATED_ALIAS = - detail::arg_formatter; + detail::arg_formatter; /** An error returned by an operating system or a language runtime, @@ -3445,11 +3450,11 @@ std::basic_string to_string(const basic_memory_buffer& buf) { } template -typename buffer_context::iterator detail::vformat_to( +detail::buffer_appender detail::vformat_to( detail::buffer& buf, basic_string_view format_str, basic_format_args>> args) { using af = arg_formatter::iterator, Char>; - return vformat_to(std::back_inserter(buf), to_string_view(format_str), + return vformat_to(buffer_appender(buf), to_string_view(format_str), args); } diff --git a/include/fmt/locale.h b/include/fmt/locale.h index 988d15cd..8f1c7280 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -21,7 +21,7 @@ typename buffer_context::iterator vformat_to( basic_string_view format_str, basic_format_args>> args) { using af = arg_formatter::iterator, Char>; - return vformat_to(std::back_inserter(buf), to_string_view(format_str), + return vformat_to(buffer_appender(buf), to_string_view(format_str), args, detail::locale_ref(loc)); } diff --git a/test/core-test.cc b/test/core-test.cc index 8a1ea83b..2655d78c 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -53,10 +53,7 @@ template struct formatter { return ctx.begin(); } - typedef std::back_insert_iterator> iterator; - - auto format(test_struct, basic_format_context& ctx) - -> decltype(ctx.out()) { + auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) { const Char* test = "test"; return std::copy_n(test, std::strlen(test), ctx.out()); } @@ -377,9 +374,9 @@ struct check_custom { test_buffer() : fmt::detail::buffer(data, 0, 10) {} void grow(size_t) {} } buffer; - fmt::detail::buffer& base = buffer; fmt::format_parse_context parse_ctx(""); - fmt::format_context ctx(std::back_inserter(base), fmt::format_args()); + fmt::format_context ctx{ + fmt::detail::buffer_appender(buffer), fmt::format_args()}; h.format(parse_ctx, ctx); EXPECT_EQ("test", std::string(buffer.data, buffer.size())); return test_result(); @@ -388,8 +385,8 @@ struct check_custom { TEST(ArgTest, CustomArg) { test_struct test; - typedef mock_visitor::handle> - visitor; + using visitor = + mock_visitor::handle>; testing::StrictMock v; EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); fmt::visit_format_arg(v, make_arg(test)); diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 1b0c1e13..f378bd78 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -39,10 +39,9 @@ class custom_arg_formatter std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) { fmt::memory_buffer buffer; - fmt::detail::buffer& base = buffer; // Pass custom argument formatter as a template arg to vwrite. - fmt::vformat_to(std::back_inserter(base), format_str, - args); + fmt::vformat_to( + fmt::detail::buffer_appender(buffer), format_str, args); return std::string(buffer.data(), buffer.size()); } diff --git a/test/format b/test/format index 11c58b96..76ac5476 100644 --- a/test/format +++ b/test/format @@ -38,9 +38,9 @@ namespace std { template class basic_format_context; using format_context = basic_format_context< - /* unspecified */ std::back_insert_iterator>, char>; + /* unspecified */ fmt::detail::buffer_appender, char>; using wformat_context = basic_format_context< - /* unspecified */ std::back_insert_iterator>, wchar_t>; + /* unspecified */ fmt::detail::buffer_appender, wchar_t>; template struct formatter { formatter() = delete; @@ -714,7 +714,7 @@ string vformat(string_view fmt, format_args args) { fmt::detail::buffer& buf = mbuf; using af = detail::arg_formatter; detail::format_handler - h(std::back_inserter(buf), fmt, args, {}); + h(fmt::detail::buffer_appender(buf), fmt, args, {}); fmt::detail::parse_format_string(fmt::to_string_view(fmt), h); return to_string(mbuf); } diff --git a/test/format-test.cc b/test/format-test.cc index 978feef2..442ad7a7 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1844,10 +1844,9 @@ class mock_arg_formatter }; static void custom_vformat(fmt::string_view format_str, fmt::format_args args) { - fmt::memory_buffer buffer; - fmt::detail::buffer& base = buffer; - fmt::vformat_to(std::back_inserter(base), format_str, - args); + fmt::memory_buffer buf; + fmt::vformat_to(fmt::detail::buffer_appender(buf), + format_str, args); } template diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 1c87d46d..fccc9b0d 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -75,8 +75,8 @@ struct test_arg_formatter TEST(OStreamTest, CustomArg) { fmt::memory_buffer buffer; - fmt::detail::buffer& base = buffer; - fmt::format_context ctx(std::back_inserter(base), fmt::format_args()); + fmt::format_context ctx( + fmt::detail::buffer_appender{buffer}, fmt::format_args()); fmt::format_specs spec; test_arg_formatter af(ctx, spec); fmt::visit_format_arg(