From 8a2de0f85dd8a2f556665267efd46eb2e6916cff Mon Sep 17 00:00:00 2001 From: Alexey Ochapov Date: Sun, 8 Nov 2020 00:42:57 +0300 Subject: [PATCH] add custom back_insert_iterator with constexpr member functions --- include/fmt/core.h | 97 ++++++++++++++++++++++++++++++++++++++++---- include/fmt/format.h | 6 +-- src/format.cc | 16 ++++---- test/core-test.cc | 2 +- test/format-test.cc | 2 + 5 files changed, 104 insertions(+), 19 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0b49b726..4ab3e2ec 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -645,10 +645,17 @@ struct is_contiguous> : std::true_type {}; namespace detail { +#ifdef FMT_USE_CUSTOM_BACK_INSERT_ITERATOR +template class back_insert_iterator; +#else +template +using back_insert_iterator = std::back_insert_iterator; +#endif + // Extracts a reference to the container from back_insert_iterator. template -inline Container& get_container(std::back_insert_iterator it) { - using bi_iterator = std::back_insert_iterator; +inline Container& get_container(back_insert_iterator it) { + using bi_iterator = back_insert_iterator; struct accessor : bi_iterator { accessor(bi_iterator iter) : bi_iterator(iter) {} using bi_iterator::container; @@ -805,8 +812,83 @@ template class iterator_buffer final : public buffer { T* out() { return &*this->end(); } }; +#ifdef FMT_USE_CUSTOM_BACK_INSERT_ITERATOR +template class back_insert_iterator { + protected: + Container* container; + + public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + + using container_type = Container; + using container_value_type = typename Container::value_type; + + constexpr explicit back_insert_iterator(Container& container) + : container(std::addressof(container)) {} + + constexpr explicit back_insert_iterator( + const std::back_insert_iterator& bi_iterator) { + using iterator = std::back_insert_iterator; + struct accessor : iterator { + explicit accessor(iterator iter) : iterator(iter) {} + using iterator::container; + }; + container = accessor(bi_iterator).container; + } + + constexpr back_insert_iterator& operator=(const container_value_type& value) { + container->push_back(value); + return *this; + } + + constexpr back_insert_iterator& operator=(container_value_type&& value) { + container->push_back(std::move(value)); + return *this; + } + + constexpr back_insert_iterator& operator*() { return *this; } + constexpr back_insert_iterator& operator++() { return *this; } + constexpr back_insert_iterator operator++(int) { return *this; } +}; + +template +constexpr back_insert_iterator back_inserter(Container& container) { + return back_insert_iterator(container); +} +#endif + // A buffer that writes to a container with the contiguous storage. template +class iterator_buffer, + enable_if_t::value, + typename Container::value_type>> + final : public buffer { + private: + Container& container_; + + protected: + void grow(size_t capacity) final FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer(c.size()), container_(c) {} + explicit iterator_buffer(back_insert_iterator out, size_t = 0) + : iterator_buffer(get_container(out)) {} + back_insert_iterator out() { + return back_inserter(container_); + } +}; + +#if FMT_USE_CUSTOM_BACK_INSERT_ITERATOR +// Explicit instantiation for std::back_insert_iterator +template class iterator_buffer, enable_if_t::value, typename Container::value_type>> @@ -824,11 +906,12 @@ class iterator_buffer, explicit iterator_buffer(Container& c) : buffer(c.size()), container_(c) {} explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) - : iterator_buffer(get_container(out)) {} + : iterator_buffer(get_container(back_insert_iterator{out})) {} std::back_insert_iterator out() { return std::back_inserter(container_); } }; +#endif // A buffer that counts the number of code units written discarding the output. template class counting_buffer final : public buffer { @@ -853,8 +936,8 @@ template class counting_buffer final : public buffer { // 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> { - using base = std::back_insert_iterator>; +class buffer_appender : public back_insert_iterator> { + using base = back_insert_iterator>; public: explicit buffer_appender(buffer& buf) : base(buf) {} @@ -1385,13 +1468,13 @@ struct is_output_iterator< template struct is_back_insert_iterator : std::false_type {}; template -struct is_back_insert_iterator> +struct is_back_insert_iterator> : std::true_type {}; template struct is_contiguous_back_insert_iterator : std::false_type {}; template -struct is_contiguous_back_insert_iterator> +struct is_contiguous_back_insert_iterator> : is_contiguous {}; template struct is_contiguous_back_insert_iterator> diff --git a/include/fmt/format.h b/include/fmt/format.h index 9970444c..fa689940 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -376,7 +376,7 @@ template ::value)> __attribute__((no_sanitize("undefined"))) #endif inline checked_ptr -reserve(std::back_insert_iterator it, size_t n) { +reserve(back_insert_iterator it, size_t n) { Container& c = get_container(it); size_t size = c.size(); c.resize(size + n); @@ -407,8 +407,8 @@ template T* to_pointer(buffer_appender it, size_t n) { } template ::value)> -inline std::back_insert_iterator base_iterator( - std::back_insert_iterator& it, +inline back_insert_iterator base_iterator( + back_insert_iterator& it, checked_ptr) { return it; } diff --git a/src/format.cc b/src/format.cc index bca87b03..b41d0a36 100644 --- a/src/format.cc +++ b/src/format.cc @@ -31,27 +31,27 @@ template dragonbox::decimal_fp dragonbox::to_decimal(double x) // DEPRECATED! This function exists for ABI compatibility. template -typename basic_format_context>, +typename basic_format_context>, Char>::iterator vformat_to(buffer& buf, basic_string_view format_str, basic_format_args>>, + back_insert_iterator>>, type_identity_t>> args) { - using iterator = std::back_insert_iterator>; - using context = basic_format_context< - std::back_insert_iterator>>, - type_identity_t>; + using iterator = back_insert_iterator>; + using context = + basic_format_context>>, + type_identity_t>; auto out = iterator(buf); format_handler h(out, format_str, args, {}); parse_format_string(format_str, h); return out; } -template basic_format_context>, +template basic_format_context>, char>::iterator vformat_to(buffer&, string_view, basic_format_args>>, + back_insert_iterator>>, type_identity_t>>); } // namespace detail diff --git a/test/core-test.cc b/test/core-test.cc index 67456399..9c71c9dd 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -292,7 +292,7 @@ VISIT_TYPE(unsigned long, unsigned long long); { \ testing::StrictMock> visitor; \ EXPECT_CALL(visitor, visit(expected)); \ - using iterator = std::back_insert_iterator>; \ + using iterator = fmt::detail::back_insert_iterator>; \ fmt::visit_format_arg( \ visitor, make_arg>(value)); \ } diff --git a/test/format-test.cc b/test/format-test.cc index 1f9a208f..79298930 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -175,6 +175,8 @@ TEST(IteratorTest, IsOutputIterator) { EXPECT_TRUE( (fmt::detail::is_output_iterator, char>::value)); + EXPECT_TRUE((fmt::detail::is_output_iterator< + fmt::detail::back_insert_iterator, char>::value)); EXPECT_TRUE( (fmt::detail::is_output_iterator::value)); EXPECT_FALSE((fmt::detail::is_output_iterator