From 33e7ed1eb5bbbd80ab4efb5495dff54dd1d83de6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 13 Jul 2024 07:23:02 -0700 Subject: [PATCH] Improve handling of back_insert_iterator that writes into a buffer --- include/fmt/base.h | 31 +++++++++++++++++++++++++++++-- test/base-test.cc | 12 +++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index c95ca6a4..e3fc5c3e 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -474,8 +474,13 @@ FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) return 0; } +namespace detect { + +using namespace std; + template struct is_back_insert_iterator : std::false_type {}; + template struct is_back_insert_iterator< It, @@ -483,6 +488,10 @@ struct is_back_insert_iterator< decltype(back_inserter(std::declval())), It>::value>> : std::true_type {}; +} // namespace detect + +using detect::is_back_insert_iterator; + // Extracts a reference to the container from *insert_iterator. template inline auto get_container(OutputIt it) -> typename OutputIt::container_type& { @@ -1158,6 +1167,7 @@ template class basic_appender { using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; + using container_type = detail::buffer; FMT_UNCHECKED_ITERATOR(basic_appender); FMT_CONSTEXPR basic_appender(detail::buffer& buf) : buffer_(&buf) {} @@ -1174,6 +1184,10 @@ template class basic_appender { using appender = basic_appender; namespace detail { +namespace detect { +template +struct is_back_insert_iterator> : std::true_type {}; +} template struct locking : std::true_type {}; @@ -1239,12 +1253,25 @@ constexpr auto has_const_formatter() -> bool { return has_const_formatter_impl(static_cast(nullptr)); } +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + // Maps an output iterator to a buffer. -template +template ::value)> auto get_buffer(OutputIt out) -> iterator_buffer { return iterator_buffer(out); } -template auto get_buffer(basic_appender out) -> buffer& { +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { return get_container(out); } diff --git a/test/base-test.cc b/test/base-test.cc index 7f0a6abf..76570036 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -114,7 +114,7 @@ TEST(base_test, is_back_insert_iterator) { TEST(base_test, buffer_appender) { #ifdef __cpp_lib_ranges - static_assert(std::output_iterator); + EXPECT_TRUE((std::output_iterator)); #endif } @@ -268,6 +268,16 @@ TEST(buffer_test, append_allocates_enough_storage) { buffer.append(test, test + 9); } +TEST(base_test, get_buffer) { + mock_buffer buffer; + void* buffer_ptr = &buffer; + auto&& appender_result = fmt::detail::get_buffer(fmt::appender(buffer)); + EXPECT_EQ(&appender_result, buffer_ptr); + auto&& back_inserter_result = + fmt::detail::get_buffer(std::back_inserter(buffer)); + EXPECT_EQ(&back_inserter_result, buffer_ptr); +} + struct custom_context { using char_type = char; using parse_context_type = fmt::format_parse_context;