Skip to content

Commit

Permalink
fix: 'format_to_n' compiles 'std::back_inserter' arguments
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DanielaE committed Oct 24, 2018
1 parent 2d2326a commit 58ab925
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
40 changes: 39 additions & 1 deletion include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -740,8 +740,12 @@ class counting_iterator {

// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt, typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;

template <typename OutputIt>
class truncating_iterator {
class truncating_iterator<OutputIt, std::false_type> {
private:
typedef std::iterator_traits<OutputIt> traits;

Expand Down Expand Up @@ -779,6 +783,40 @@ class truncating_iterator {
reference operator*() const { return count_ < limit_ ? *out_ : blackhole_; }
};

template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type> {
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 <typename T>
Expand Down
12 changes: 12 additions & 0 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <cstring>
#include <list>
#include <memory>
#include <string>
#include <stdint.h>

// Check if fmt/format.h compiles with windows.h included before it.
Expand Down Expand Up @@ -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<decltype(bi)> it(bi, 2);
*it++ = '4';
*it++ = '2';
*it++ = '1';
EXPECT_EQ(buffer.size(), 2);
EXPECT_EQ(buffer, "42");
}

TEST(MemoryBufferTest, Ctor) {
basic_memory_buffer<char, 123> buffer;
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
Expand Down

0 comments on commit 58ab925

Please sign in to comment.