From 1e3dcbba81ca28e5818608f71e7e19cbcd553607 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 | 72 +++++++++++++++++++++++++++++++++----------- test/format-test.cc | 12 ++++++++ 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 0d925440e19d..031436d354b0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -738,35 +738,49 @@ class counting_iterator { T &operator*() const { return blackhole_; } }; +template +class truncating_iterator_base { + protected: + OutputIt out_; + std::size_t limit_; + std::size_t count_; + + truncating_iterator_base(OutputIt out, std::size_t limit) + : out_(out), limit_(limit), count_(0) {} + + public: + typedef std::output_iterator_tag iterator_category; + typedef void difference_type; + typedef void pointer; + typedef void reference; + typedef truncating_iterator_base _Unchecked_type; // Mark iterator as checked. + + OutputIt base() const { return out_; } + std::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 { - private: +class truncating_iterator: + public truncating_iterator_base { typedef std::iterator_traits traits; - OutputIt out_; - std::size_t limit_; - std::size_t count_; mutable typename traits::value_type blackhole_; public: - typedef std::output_iterator_tag iterator_category; typedef typename traits::value_type value_type; - typedef typename traits::difference_type difference_type; - typedef typename traits::pointer pointer; - typedef typename traits::reference 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_base(out, limit) {} truncating_iterator& operator++() { - if (count_++ < limit_) - ++out_; + if (this->count_++ < this->limit_) + ++this->out_; return *this; } @@ -776,7 +790,31 @@ class truncating_iterator { return it; } - reference operator*() const { return count_ < limit_ ? *out_ : blackhole_; } + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator: + public truncating_iterator_base { + public: + typedef typename OutputIt::container_type::value_type value_type; + + truncating_iterator(OutputIt out, std::size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator=(value_type 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; } }; // Returns true if value is negative, false otherwise. diff --git a/test/format-test.cc b/test/format-test.cc index f05e3acb1e85..4157ad68f616 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());