Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 'format_to_n' compiles 'std::back_inserter' arguments #913

Merged
merged 1 commit into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 55 additions & 17 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,35 +738,49 @@ class counting_iterator {
T &operator*() const { return blackhole_; }
};

template <typename OutputIt>
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 <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 {
private:
class truncating_iterator<OutputIt, std::false_type>:
public truncating_iterator_base<OutputIt> {
typedef std::iterator_traits<OutputIt> 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<OutputIt>(out, limit) {}

truncating_iterator& operator++() {
if (count_++ < limit_)
++out_;
if (this->count_++ < this->limit_)
++this->out_;
return *this;
}

Expand All @@ -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 <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>:
public truncating_iterator_base<OutputIt> {
public:
typedef typename OutputIt::container_type::value_type value_type;

truncating_iterator(OutputIt out, std::size_t limit)
: truncating_iterator_base<OutputIt>(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; }
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to move common code between the two specializations into a superclass to reduce copy-paste?


// Returns true if value is negative, false otherwise.
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