diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 0ce654c4..fa8eaeca 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -25,6 +25,85 @@ FMT_BEGIN_NAMESPACE namespace detail { +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_ = 0; + + truncating_iterator_base() : out_(), limit_(0) {} + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + // A compile-time string which is compiled into fast formatting code. class compiled_string {}; diff --git a/include/fmt/format.h b/include/fmt/format.h index 065aa496..ed55709c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -465,85 +465,6 @@ class counting_iterator { value_type operator*() const { return {}; } }; -template class truncating_iterator_base { - protected: - OutputIt out_; - size_t limit_; - size_t count_ = 0; - - truncating_iterator_base() : out_(), limit_(0) {} - - truncating_iterator_base(OutputIt out, size_t limit) - : out_(out), limit_(limit) {} - - public: - using iterator_category = std::output_iterator_tag; - using value_type = typename std::iterator_traits::value_type; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - using _Unchecked_type = - truncating_iterator_base; // Mark iterator as checked. - - OutputIt base() const { return out_; } - size_t count() const { return count_; } -}; - -// An output iterator that truncates the output and counts the number of objects -// written to it. -template ::value_type>::type> -class truncating_iterator; - -template -class truncating_iterator - : public truncating_iterator_base { - mutable typename truncating_iterator_base::value_type blackhole_; - - public: - using value_type = typename truncating_iterator_base::value_type; - - truncating_iterator() = default; - - truncating_iterator(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} - - truncating_iterator& operator++() { - if (this->count_++ < this->limit_) ++this->out_; - return *this; - } - - truncating_iterator operator++(int) { - auto it = *this; - ++*this; - return it; - } - - value_type& operator*() const { - return this->count_ < this->limit_ ? *this->out_ : blackhole_; - } -}; - -template -class truncating_iterator - : public truncating_iterator_base { - public: - truncating_iterator() = default; - - truncating_iterator(OutputIt out, size_t limit) - : truncating_iterator_base(out, limit) {} - - template truncating_iterator& operator=(T val) { - if (this->count_++ < this->limit_) *this->out_++ = val; - return *this; - } - - truncating_iterator& operator++() { return *this; } - truncating_iterator& operator++(int) { return *this; } - truncating_iterator& operator*() { return *this; } -}; - // is spectacularly slow to compile in C++20 so use a simple fill_n // instead (#1998). template diff --git a/test/compile-test.cc b/test/compile-test.cc index 6ccef2d0..d75166cc 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -19,6 +19,43 @@ #include "gtest-extra.h" #include "util.h" +TEST(IteratorTest, TruncatingIterator) { + char* p = nullptr; + fmt::detail::truncating_iterator it(p, 3); + auto prev = it++; + EXPECT_EQ(prev.base(), p); + EXPECT_EQ(it.base(), p + 1); +} + + +TEST(IteratorTest, TruncatingIteratorDefaultConstruct) { + static_assert( + std::is_default_constructible>::value, + ""); + + fmt::detail::truncating_iterator it; + EXPECT_EQ(nullptr, it.base()); + EXPECT_EQ(std::size_t{0}, it.count()); +} + +#ifdef __cpp_lib_ranges +TEST(IteratorTest, TruncatingIteratorOutputIterator) { + static_assert(std::output_iterator, + char>); +} +#endif + +TEST(IteratorTest, TruncatingBackInserter) { + std::string buffer; + auto bi = std::back_inserter(buffer); + fmt::detail::truncating_iterator it(bi, 2); + *it++ = '4'; + *it++ = '2'; + *it++ = '1'; + EXPECT_EQ(buffer.size(), 2); + EXPECT_EQ(buffer, "42"); +} + // compiletime_prepared_parts_type_provider is useful only with relaxed // constexpr. #if FMT_USE_CONSTEXPR diff --git a/test/format-test.cc b/test/format-test.cc index a71aeaf1..cdd42db5 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -151,43 +151,6 @@ TEST(IteratorTest, CountingIterator) { EXPECT_EQ((it + 41).count(), 42); } -TEST(IteratorTest, TruncatingIterator) { - char* p = nullptr; - fmt::detail::truncating_iterator it(p, 3); - auto prev = it++; - EXPECT_EQ(prev.base(), p); - EXPECT_EQ(it.base(), p + 1); -} - - -TEST(IteratorTest, TruncatingIteratorDefaultConstruct) { - static_assert( - std::is_default_constructible>::value, - ""); - - fmt::detail::truncating_iterator it; - EXPECT_EQ(nullptr, it.base()); - EXPECT_EQ(std::size_t{0}, it.count()); -} - -#ifdef __cpp_lib_ranges -TEST(IteratorTest, TruncatingIteratorOutputIterator) { - static_assert(std::output_iterator, - char>); -} -#endif - -TEST(IteratorTest, TruncatingBackInserter) { - std::string buffer; - auto bi = std::back_inserter(buffer); - fmt::detail::truncating_iterator it(bi, 2); - *it++ = '4'; - *it++ = '2'; - *it++ = '1'; - EXPECT_EQ(buffer.size(), 2); - EXPECT_EQ(buffer, "42"); -} - TEST(IteratorTest, IsOutputIterator) { EXPECT_TRUE((fmt::detail::is_output_iterator::value)); EXPECT_FALSE((fmt::detail::is_output_iterator::value)); diff --git a/test/printf-test.cc b/test/printf-test.cc index ccd72dcd..1c1173c4 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -606,23 +606,3 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) { {fmt::make_wprintf_args(42, L"something")})); #endif } - -TEST(PrintfTest, PrintfDetermineOutputSize) { - using backit = std::back_insert_iterator>; - using truncated_printf_context = - fmt::basic_printf_context, char>; - - auto v = std::vector{}; - auto it = std::back_inserter(v); - - const auto format_string = "%s"; - const auto format_arg = "Hello"; - const auto expected_size = fmt::sprintf(format_string, format_arg).size(); - - EXPECT_EQ((truncated_printf_context( - fmt::detail::truncating_iterator(it, 0), format_string, - fmt::make_format_args(format_arg)) - .format() - .count()), - expected_size); -}