Skip to content

Commit

Permalink
Move detail::truncating_iterator to fmt/compile.h
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Mar 6, 2021
1 parent e718ec3 commit 6e1fc01
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 136 deletions.
79 changes: 79 additions & 0 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,85 @@
FMT_BEGIN_NAMESPACE
namespace detail {

template <typename OutputIt> 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<OutputIt>::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 <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<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;

public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;

truncating_iterator() = default;

truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(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 <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;

truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}

template <typename T> 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 {};

Expand Down
79 changes: 0 additions & 79 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,85 +465,6 @@ class counting_iterator {
value_type operator*() const { return {}; }
};

template <typename OutputIt> 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<OutputIt>::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 <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<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;

public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;

truncating_iterator() = default;

truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(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 <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;

truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}

template <typename T> 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; }
};

// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
// instead (#1998).
template <typename OutputIt, typename Size, typename T>
Expand Down
37 changes: 37 additions & 0 deletions test/compile-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@
#include "gtest-extra.h"
#include "util.h"

TEST(IteratorTest, TruncatingIterator) {
char* p = nullptr;
fmt::detail::truncating_iterator<char*> 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<fmt::detail::truncating_iterator<char*>>::value,

This comment has been minimized.

Copy link
@alexezeder

alexezeder Mar 10, 2021

Contributor

code style is not applied here

This comment has been minimized.

Copy link
@vitaut

vitaut Mar 10, 2021

Author Contributor

Applied clang-format in f9e0e90, thanks.

"");

fmt::detail::truncating_iterator<char*> 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<fmt::detail::truncating_iterator<char*>,
char>);
}
#endif

TEST(IteratorTest, TruncatingBackInserter) {
std::string buffer;
auto bi = std::back_inserter(buffer);
fmt::detail::truncating_iterator<decltype(bi)> 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
Expand Down
37 changes: 0 additions & 37 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,43 +151,6 @@ TEST(IteratorTest, CountingIterator) {
EXPECT_EQ((it + 41).count(), 42);
}

TEST(IteratorTest, TruncatingIterator) {
char* p = nullptr;
fmt::detail::truncating_iterator<char*> 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<fmt::detail::truncating_iterator<char*>>::value,
"");

fmt::detail::truncating_iterator<char*> 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<fmt::detail::truncating_iterator<char*>,
char>);
}
#endif

TEST(IteratorTest, TruncatingBackInserter) {
std::string buffer;
auto bi = std::back_inserter(buffer);
fmt::detail::truncating_iterator<decltype(bi)> 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<char*, char>::value));
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
Expand Down
20 changes: 0 additions & 20 deletions test/printf-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -606,23 +606,3 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
{fmt::make_wprintf_args(42, L"something")}));
#endif
}

TEST(PrintfTest, PrintfDetermineOutputSize) {
using backit = std::back_insert_iterator<std::vector<char>>;
using truncated_printf_context =
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;

auto v = std::vector<char>{};
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<backit>(it, 0), format_string,
fmt::make_format_args<truncated_printf_context>(format_arg))
.format()
.count()),
expected_size);
}

0 comments on commit 6e1fc01

Please sign in to comment.