From fd16204bf37fb329764dbb03144ce36e1115e634 Mon Sep 17 00:00:00 2001 From: rimathia Date: Thu, 5 Nov 2020 13:08:23 +0100 Subject: [PATCH] eliminate one case where basic_print_context would copy a string into a fmt::basic_memory_buffer character by character instead of using fmt::basic_memory_buffer::append --- include/fmt/printf.h | 40 +++++++++++++++++++++++++++++++++++++--- test/printf-test.cc | 22 +++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/include/fmt/printf.h b/include/fmt/printf.h index b44afabac0ef..ae64484b0753 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -330,6 +330,40 @@ template struct printf_formatter { } }; +namespace detail { +template +struct has_container_append : std::false_type {}; + +template +struct has_container_append< + OutputIt, + void_t()) + .append(std::declval< + const typename OutputIt::container_type::value_type*>(), + std::declval()))>> : std::true_type {}; + +template +enable_if_t::value, OutputIt> +container_aware_copy(const typename OutputIt::container_type::value_type* first, + const typename OutputIt::container_type::value_type* last, + OutputIt out) { + detail::get_container(out).append(first, last); + return out; +} + +static_assert(has_container_append>::value, + "has_container_append doesn't work"); + +template +enable_if_t::value, OutputIt> +container_aware_copy(const Char* first, const Char* last, OutputIt out) { + return std::copy(first, last, out); +} + +} // namespace detail + /** This template formats data and writes the output through an output iterator. */ @@ -480,11 +514,11 @@ OutputIt basic_printf_context::format() { } char_type c = *it++; if (it != end && *it == c) { - out = std::copy(start, it, out); + out = container_aware_copy(start, it, out); start = ++it; continue; } - out = std::copy(start, it - 1, out); + out = container_aware_copy(start, it - 1, out); format_specs specs; specs.align = align::right; @@ -596,7 +630,7 @@ OutputIt basic_printf_context::format() { // Format argument. out = visit_format_arg(ArgFormatter(out, specs, *this), arg); } - return std::copy(start, it, out); + return container_aware_copy(start, it, out); } template diff --git a/test/printf-test.cc b/test/printf-test.cc index ccd72dcd7522..9b806002e38c 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -619,10 +619,26 @@ TEST(PrintfTest, PrintfDetermineOutputSize) { const auto format_arg = "Hello"; const auto expected_size = fmt::sprintf(format_string, format_arg).size(); - EXPECT_EQ((truncated_printf_context( + EXPECT_EQ(expected_size, + (truncated_printf_context( fmt::detail::truncating_iterator(it, 0), format_string, fmt::make_format_args(format_arg)) .format() - .count()), - expected_size); + .count())); +} + +TEST(PrintfTest, PrintfAppendToBuffer) { + using backit = std::back_insert_iterator>; + using context = fmt::basic_printf_context; + + const auto format_string = "%s"; + const char* format_arg = "Hello"; + fmt::basic_memory_buffer buffer; + context(std::back_inserter(buffer), format_string, + fmt::make_format_args(format_arg)) + .format(); + + std::string result(std::begin(buffer), std::end(buffer)); + + EXPECT_EQ(std::string("Hello"), result); }