From 58ab92511a1edd17e52157d74277fed747315718 Mon Sep 17 00:00:00 2001 From: Daniela Engert Date: Wed, 24 Oct 2018 08:02:15 +0200 Subject: [PATCH] fix: 'format_to_n' compiles 'std::back_inserter' arguments std::back_insert_iterators model the OutputIterator concept but differ considerably in their traits and behavior. In particular the former made compilation to fail when format_to_n is given a back_inserter as first argument. The emulation of an OutputIterator is not perfect due to the behavioural differences of back_insert_iterators (e.g. assignment always implies increment) but good enough to be used within fmt's machinery. --- include/fmt/format.h | 40 +++++++++++++++++++++++++++++++++++++++- test/format-test.cc | 12 ++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 428e4a29b0488..c9e76e5ebffb0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -740,8 +740,12 @@ class counting_iterator { // 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 { +class truncating_iterator { private: typedef std::iterator_traits traits; @@ -779,6 +783,40 @@ class truncating_iterator { reference operator*() const { return count_ < limit_ ? *out_ : blackhole_; } }; +template +class truncating_iterator { + private: + OutputIt out_; + std::size_t limit_; + std::size_t count_; + + public: + typedef std::output_iterator_tag iterator_category; + typedef typename OutputIt::container_type::value_type value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + typedef truncating_iterator _Unchecked_type; // Mark iterator as checked. + + truncating_iterator(OutputIt out, std::size_t limit) + : out_(out), limit_(limit), count_(0) {} + + OutputIt base() const { return out_; } + std::size_t count() const { return count_; } + + truncating_iterator& operator=(value_type val) { + if (count_++ < limit_) + out_ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + + truncating_iterator& operator++(int) { return *this; } + + truncating_iterator& operator*() { return *this; } +}; + // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template diff --git a/test/format-test.cc b/test/format-test.cc index 2f227bf0f2e76..1f90e3969de18 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include // Check if fmt/format.h compiles with windows.h included before it. @@ -185,6 +186,17 @@ TEST(IteratorTest, TruncatingIterator) { EXPECT_EQ(it.base(), p + 1); } +TEST(IteratorTest, TruncatingBackInserter) { + std::string buffer; + auto bi = std::back_inserter(buffer); + fmt::internal::truncating_iterator it(bi, 2); + *it++ = '4'; + *it++ = '2'; + *it++ = '1'; + EXPECT_EQ(buffer.size(), 2); + EXPECT_EQ(buffer, "42"); +} + TEST(MemoryBufferTest, Ctor) { basic_memory_buffer buffer; EXPECT_EQ(static_cast(0), buffer.size());