From 44207cdd947dc765d29951384823ac4fbafb29ed Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sat, 9 Mar 2019 13:17:59 -0500 Subject: [PATCH 001/102] Puts main.cpp in own bazel cc_library Fixes #53 --- test/BUILD | 6 ++++++ test/make_tests.bzl | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/BUILD b/test/BUILD index 5c7c5d9c..b7bdc149 100644 --- a/test/BUILD +++ b/test/BUILD @@ -36,4 +36,10 @@ progs = [ "helpers", ] +cc_library( + name = "test_main", + srcs = ["test_main.cpp", "catch.hpp"], + copts = ["-std=c++17", "-g"] +) + itertools_tests(progs) diff --git a/test/make_tests.bzl b/test/make_tests.bzl index 883f0576..68a428b8 100644 --- a/test/make_tests.bzl +++ b/test/make_tests.bzl @@ -2,7 +2,10 @@ def itertools_tests(progs): for p in progs: native.cc_test( name = "test_{}".format(p), - srcs = ["test_{}.cpp".format(p), "test_main.cpp", "catch.hpp", "helpers.hpp"], - deps = ["//:cppitertools",], - copts = ["-I.", "-std=c++17", "-Wall", "-Wextra", "-pedantic", "-g"], + srcs = ["test_{}.cpp".format(p), "catch.hpp", "helpers.hpp"], + deps = [ + "//:cppitertools", + ":test_main", + ], + copts = ["-I.", "-std=c++17", "-Wall", "-Wextra", "-pedantic", "-g"], ) From b7a3c38a388da3f24714c73dd7d34c823d80043d Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 22 Mar 2019 18:10:24 -0400 Subject: [PATCH 002/102] Adds range .start() .step() and .stop() First part of #55 --- range.hpp | 22 +++++++++++++------ test/test_range.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/range.hpp b/range.hpp index d4727b4b..c30f35f0 100644 --- a/range.hpp +++ b/range.hpp @@ -124,6 +124,18 @@ class iter::impl::Range { : start_{start}, stop_{stop}, step_{step} {} public: + constexpr T start() const noexcept { + return start_; + } + + constexpr T stop() const noexcept { + return stop_; + } + + constexpr T step() const noexcept { + return step_; + } + // the reference type here is T, which doesn't strictly follow all // of the rules, but std::vector::iterator::reference isn't // a reference type either, this isn't any worse @@ -139,11 +151,9 @@ class iter::impl::Range { const Iterator& lhs, const Iterator& rhs) noexcept { assert(!lhs.is_end); assert(rhs.is_end); - if - constexpr(std::is_unsigned{}) { - return lhs.data.value() < rhs.data.value(); - } - else { + if constexpr (std::is_unsigned{}) { + return lhs.data.value() < rhs.data.value(); + } else { return !(lhs.data.step() > 0 && lhs.data.value() >= rhs.data.value()) && !(lhs.data.step() < 0 && lhs.data.value() <= rhs.data.value()); @@ -183,7 +193,7 @@ class iter::impl::Range { return *this; } - Iterator operator++(int)noexcept { + Iterator operator++(int) noexcept { auto ret = *this; ++*this; return ret; diff --git a/test/test_range.cpp b/test/test_range.cpp index dab748e3..23833e03 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -10,6 +10,58 @@ using Vec = const std::vector; using iter::range; +TEST_CASE("range: .start(), .stop(), and .step()", "[range]") { + SECTION("one arg") { + auto r = range(3); + REQUIRE(r.start() == 0); + REQUIRE(r.stop() == 3); + REQUIRE(r.step() == 1); + + // make sure iterators aren't changing the value + auto it = r.begin(); + ++it; + + REQUIRE(r.start() == 0); + REQUIRE(r.stop() == 3); + REQUIRE(r.step() == 1); + } + + SECTION("two args") { + auto r = range(2, 10); + REQUIRE(r.start() == 2); + REQUIRE(r.stop() == 10); + REQUIRE(r.step() == 1); + } + + SECTION("three args") { + auto r = range(-6, 20, 3); + REQUIRE(r.start() == -6); + REQUIRE(r.stop() == 20); + REQUIRE(r.step() == 3); + } + + SECTION("one arg (double)") { + auto r = range(3.5); + REQUIRE(r.start() == 0); + REQUIRE(r.stop() == Approx(3.5)); + REQUIRE(r.step() == Approx(1.0)); + } + + SECTION("two args (double)") { + auto r = range(20.1, 31.7); + REQUIRE(r.start() == Approx(20.1)); + REQUIRE(r.stop() == Approx(31.7)); + REQUIRE(r.step() == Approx(1.0)); + } + + SECTION("three args (double)") { + auto r = range(-6.3, 5.7, 0.1); + REQUIRE(r.start() == Approx(-6.3)); + REQUIRE(r.stop() == Approx(5.7)); + REQUIRE(r.step() == Approx(0.1)); + } +} + TEST_CASE("range: works with only stop", "[range]") { auto r = range(5); Vec v(std::begin(r), std::end(r)); From 449d9de526f8b953e2ec307946a43f5f5a2e09cd Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 14 Apr 2019 01:32:46 -0400 Subject: [PATCH 003/102] Updates catch version to 2.6.0 --- test/download_catch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/download_catch.sh b/test/download_catch.sh index 0cae9fd7..487e1233 100755 --- a/test/download_catch.sh +++ b/test/download_catch.sh @@ -1,2 +1,2 @@ #!/usr/bin/env sh -wget -c https://github.com/catchorg/Catch2/releases/download/v2.4.2/catch.hpp +wget -c https://github.com/catchorg/Catch2/releases/download/v2.6.0/catch.hpp From a24fcdcf40dd76ce5b46cde363be8c3231e7b940 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 14 Apr 2019 01:34:37 -0400 Subject: [PATCH 004/102] Adds .size() to range Second Part of #55. Not supporting floating point types. I can't yet get the float version perfect. --- range.hpp | 37 ++++++++++++++++++++++++++++++------- test/test_range.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/range.hpp b/range.hpp index c30f35f0..fb222c7c 100644 --- a/range.hpp +++ b/range.hpp @@ -123,6 +123,17 @@ class iter::impl::Range { constexpr Range(T start, T stop, T step = 1) noexcept : start_{start}, stop_{stop}, step_{step} {} + // if val is "before" the stopping point. + static constexpr bool is_within_range( + T val, T stop_val, [[maybe_unused]] T step_val) { + if constexpr (std::is_unsigned{}) { + return val < stop_val; + } else { + return !(step_val > 0 && val >= stop_val) + && !(step_val < 0 && val <= stop_val); + } + } + public: constexpr T start() const noexcept { return start_; @@ -136,6 +147,23 @@ class iter::impl::Range { return step_; } + constexpr std::size_t size() const noexcept { + static_assert(!std::is_floating_point_v, + "range size() not supperted with floating point types"); + if (!is_within_range(start(), stop(), step())) { + return 0; + } + + auto diff = stop() - start(); + auto res = diff / step(); + assert(res >= 0); + auto result = static_cast(res); + if (diff % step()) { + ++result; + } + return result; + } + // the reference type here is T, which doesn't strictly follow all // of the rules, but std::vector::iterator::reference isn't // a reference type either, this isn't any worse @@ -151,13 +179,8 @@ class iter::impl::Range { const Iterator& lhs, const Iterator& rhs) noexcept { assert(!lhs.is_end); assert(rhs.is_end); - if constexpr (std::is_unsigned{}) { - return lhs.data.value() < rhs.data.value(); - } else { - return !(lhs.data.step() > 0 && lhs.data.value() >= rhs.data.value()) - && !(lhs.data.step() < 0 - && lhs.data.value() <= rhs.data.value()); - } + return is_within_range( + lhs.data.value(), rhs.data.value(), lhs.data.step()); } static bool not_equal_to_end( diff --git a/test/test_range.cpp b/test/test_range.cpp index 23833e03..7bfa85ac 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -312,3 +312,42 @@ TEST_CASE("range: iterator meets forward iterator requirements", "[range]") { REQUIRE(itertest::IsForwardIterator::value); REQUIRE(itertest::IsForwardIterator::value); } + +TEMPLATE_TEST_CASE("range: .size() with signed integrals", "[range]", + signed char, short, int, long, long long) { + constexpr TestType N = 5; + constexpr TestType INC = 1; + for (TestType start = -N; start < N; start += INC) { + for (TestType stop = -N; stop < N; stop += INC) { + for (TestType step = -N; step < N; step += INC) { + if (step == 0) { + continue; + } + auto r = range(start, stop, step); + REQUIRE(r.size() + == static_cast( + std::distance(std::begin(r), std::end(r)))); + } + } + } +} + +TEMPLATE_TEST_CASE("range: .size() with unsigned integrals", "[range]", + unsigned char, unsigned short, unsigned int, unsigned long, + unsigned long long) { + constexpr TestType N = 5; + constexpr TestType INC = 1; + for (TestType start = 0; start < N; start += INC) { + for (TestType stop = 0; stop < N; stop += INC) { + for (TestType step = 0; step < N; step += INC) { + if (step == 0) { + continue; + } + auto r = range(start, stop, step); + REQUIRE(r.size() + == static_cast( + std::distance(std::begin(r), std::end(r)))); + } + } + } +} From 0e1765270dbb7254416e251f8764c2895977e236 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 14 Apr 2019 01:49:25 -0400 Subject: [PATCH 005/102] Adds range operator[] To the range object, not to the iterator. --- range.hpp | 4 ++++ test/test_range.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/range.hpp b/range.hpp index fb222c7c..2efa7396 100644 --- a/range.hpp +++ b/range.hpp @@ -147,6 +147,10 @@ class iter::impl::Range { return step_; } + constexpr T operator[](std::size_t index) const noexcept { + return start() + (step() * index); + } + constexpr std::size_t size() const noexcept { static_assert(!std::is_floating_point_v, "range size() not supperted with floating point types"); diff --git a/test/test_range.cpp b/test/test_range.cpp index 7bfa85ac..4a25211a 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -313,6 +313,38 @@ TEST_CASE("range: iterator meets forward iterator requirements", "[range]") { REQUIRE(itertest::IsForwardIterator::value); } +TEST_CASE("range: operator[] simple tests", "[range]") { + SECTION("range(start)") { + auto r = range(4); + REQUIRE(r[0] == 0); + REQUIRE(r[1] == 1); + REQUIRE(r[2] == 2); + REQUIRE(r[3] == 3); + } + SECTION("range(start, stop)") { + auto r = range(10, 14); + REQUIRE(r[0] == 10); + REQUIRE(r[1] == 11); + REQUIRE(r[2] == 12); + REQUIRE(r[3] == 13); + } + SECTION("range(start, stop, step)") { + auto r = range(20, 30, 3); + REQUIRE(r[0] == 20); + REQUIRE(r[1] == 23); + REQUIRE(r[2] == 26); + REQUIRE(r[3] == 29); + } + SECTION("range(start, stop, step) with double") { + auto r = range(50.0, 50.99, 0.2); + REQUIRE(r[0] == Approx(50.0)); + REQUIRE(r[1] == Approx(50.2)); + REQUIRE(r[2] == Approx(50.4)); + REQUIRE(r[3] == Approx(50.6)); + REQUIRE(r[4] == Approx(50.8)); + } +} + TEMPLATE_TEST_CASE("range: .size() with signed integrals", "[range]", signed char, short, int, long, long long) { constexpr TestType N = 5; From 97bfd33cdc268426b20f189c13d3ed88f5e1f4c2 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 14 Apr 2019 02:08:41 -0400 Subject: [PATCH 006/102] Adds range updates to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index d53c8da9..1c85fd38 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,12 @@ recomputed at each step to avoid accumulating floating point inaccuracies (`value = start + (step * steps_taken`). The result of the latter is a bit slower but more accurate. +`range` also supports the following operations: + - `.size()` to get the number of elements in the range (not enabled for + floating point ranges). + - Accessors for `.start()`, `.stop()`, and `.step()`. + - Indexing. Given a range `r`, `r[n]` is the `n`th element in the range. + enumerate --------- From f6573bd394a24c5febf2943453d354d2adacaad8 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 7 Jun 2019 00:00:34 -0400 Subject: [PATCH 007/102] Small fix and cleanup to DerefHolder get_ptr() wasn't valid for DerefHolder with values --- internal/iterbase.hpp | 29 +++++++++++++++++++---------- test/test_iterbase.cpp | 3 +++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/internal/iterbase.hpp b/internal/iterbase.hpp index 7b460bf5..df1df2ba 100644 --- a/internal/iterbase.hpp +++ b/internal/iterbase.hpp @@ -7,6 +7,7 @@ // also applies to the name of the file. No user code should include // this file directly. +#include #include #include #include @@ -276,28 +277,32 @@ namespace iter { // it could still be an rvalue reference using TPlain = std::remove_reference_t; - std::optional item_p; + std::optional item_p_; public: using reference = TPlain&; using pointer = TPlain*; + static constexpr bool stores_value = true; + DerefHolder() = default; reference get() { - return *this->item_p; + assert(item_p_.has_value()); + return *item_p_; } pointer get_ptr() { - return this->item_p.get(); + assert(item_p_.has_value()); + return &item_p_.value(); } void reset(T&& item) { - item_p = std::move(item); + item_p_ = std::move(item); } explicit operator bool() const { - return static_cast(this->item_p); + return static_cast(item_p_); } }; @@ -309,25 +314,29 @@ namespace iter { using pointer = T*; private: - pointer item_p{}; + pointer item_p_{}; public: + static constexpr bool stores_value = false; + DerefHolder() = default; reference get() { - return *this->item_p; + assert(item_p_); + return *item_p_; } pointer get_ptr() { - return this->item_p; + assert(item_p_); + return item_p_; } void reset(reference item) { - this->item_p = &item; + item_p_ = &item; } explicit operator bool() const { - return this->item_p != nullptr; + return item_p_ != nullptr; } }; diff --git a/test/test_iterbase.cpp b/test/test_iterbase.cpp index 89c7fef9..c8783f32 100644 --- a/test/test_iterbase.cpp +++ b/test/test_iterbase.cpp @@ -63,6 +63,7 @@ TEST_CASE("are_same", "[iterbase]") { } TEST_CASE("DerefHolder lvalue reference", "[iterbase]") { + REQUIRE_FALSE(it::DerefHolder::stores_value); it::DerefHolder dh; int a = 2; int b = 5; @@ -78,6 +79,7 @@ TEST_CASE("DerefHolder lvalue reference", "[iterbase]") { } TEST_CASE("DerefHolder non-reference", "[iterbase]") { + REQUIRE(it::DerefHolder::stores_value); it::DerefHolder dh; int a = 2; int b = 5; @@ -85,6 +87,7 @@ TEST_CASE("DerefHolder non-reference", "[iterbase]") { dh.reset(std::move(a)); REQUIRE(dh.get() == 2); REQUIRE(&dh.get() != &a); + REQUIRE(dh.get_ptr() != &a); dh.reset(std::move(b)); REQUIRE(dh.get() == 5); From b42772dc0e35bece4eee7b30870f78013fab5703 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 7 Jun 2019 00:14:57 -0400 Subject: [PATCH 008/102] Uses shared_ptr for vector in chunked() Addresses part of #57 --- chunked.hpp | 16 +++++++++------- test/test_mixed.cpp | 33 +++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/chunked.hpp b/chunked.hpp index 87f0a0de..1d03b6e1 100644 --- a/chunked.hpp +++ b/chunked.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -45,20 +46,21 @@ class iter::impl::Chunker { private: template friend class Iterator; + std::shared_ptr> chunk_ = + std::make_shared>(); IteratorWrapper sub_iter_; IteratorWrapper sub_end_; - DerefVec chunk_; std::size_t chunk_size_ = 0; bool done() const { - return chunk_.empty(); + return chunk_->empty(); } void refill_chunk() { - chunk_.get().clear(); + chunk_->get().clear(); std::size_t i{0}; while (i < chunk_size_ && sub_iter_ != sub_end_) { - chunk_.get().push_back(sub_iter_); + chunk_->get().push_back(sub_iter_); ++sub_iter_; ++i; } @@ -76,7 +78,7 @@ class iter::impl::Chunker { : sub_iter_{std::move(sub_iter)}, sub_end_{std::move(sub_end)}, chunk_size_{s} { - chunk_.get().reserve(chunk_size_); + chunk_->get().reserve(chunk_size_); refill_chunk(); } @@ -103,11 +105,11 @@ class iter::impl::Chunker { } DerefVec& operator*() { - return chunk_; + return *chunk_; } DerefVec* operator->() { - return &chunk_; + return chunk_.get(); } }; diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index 46d47f03..4f5acc02 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -75,8 +75,8 @@ TEST_CASE("filtering doesn't dereference multiple times", "[imap][filter]") { } TEST_CASE("dropwhile doesn't dereference multiple times", "[imap][dropwhile]") { - using iter::imap; using iter::dropwhile; + using iter::imap; std::array arr = {{{41}, {42}, {43}}}; @@ -128,9 +128,9 @@ TEST_CASE("sorted(chain.from_iterable)", "[sorted][chain.from_iterable]") { } TEST_CASE("filter into enumerate with pipe", "[filter][enumerate]") { - using iter::imap; - using iter::filter; using iter::enumerate; + using iter::filter; + using iter::imap; std::array arr = {{{41}, {42}, {43}, {44}}}; auto seq = @@ -144,8 +144,33 @@ TEST_CASE("filter into enumerate with pipe", "[filter][enumerate]") { REQUIRE(v == vc); } +TEST_CASE("enumerate(filter(chunked()))", "[filter][enumerate][chunked]") { + using iter::chunked; + using iter::enumerate; + using iter::filter; + std::vector v(500); + auto chunks = chunked(v, 100); + auto filtered = filter([](auto&) { return true; }, chunks); + for (auto&& [i, chunk] : enumerate(filtered)) { + (void)i; + REQUIRE(chunk.size() == 100); + } +} + +TEST_CASE("zip(filter(chunked()))", "[filter][chunked][zip]") { + using iter::chunked; + using iter::filter; + using iter::zip; + std::vector v(500); + auto chunks = chunked(v, 100); + auto filtered = filter([](auto&) { return true; }, chunks); + for (auto&& [chunk] : zip(filtered)) { + REQUIRE(chunk.size() == 100); + } +} + TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", - "[chain.from_iterable][imap]") { + "[chain.from_iterable][imap]") { using iter::chain; using iter::imap; const std::vector> ns = {{'a'}, {'q'}, {'x', 'z'}}; From 512dbd975af2517f894d11c2b728e3669ad5d5a8 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 7 Jun 2019 00:36:00 -0400 Subject: [PATCH 009/102] Uses shared_ptr of vector in sliding_window Addresses part of #57 --- sliding_window.hpp | 14 ++++++++------ test/test_mixed.cpp | 12 ++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/sliding_window.hpp b/sliding_window.hpp index fcb41e00..d4ab6cbc 100644 --- a/sliding_window.hpp +++ b/sliding_window.hpp @@ -7,6 +7,7 @@ #include #include +#include #include namespace iter { @@ -41,8 +42,9 @@ class iter::impl::WindowSlider { private: template friend class Iterator; + std::shared_ptr> window_ = + std::make_shared>(); IteratorWrapper sub_iter_; - DerefVec window_; public: using iterator_category = std::input_iterator_tag; @@ -56,7 +58,7 @@ class iter::impl::WindowSlider { : sub_iter_(std::move(sub_iter)) { std::size_t i{0}; while (i < window_sz && sub_iter_ != sub_end) { - window_.get().push_back(sub_iter_); + window_->get().push_back(sub_iter_); ++i; if (i != window_sz) { ++sub_iter_; @@ -75,17 +77,17 @@ class iter::impl::WindowSlider { } DerefVec& operator*() { - return window_; + return *window_; } DerefVec* operator->() { - return window_; + return window_.get(); } Iterator& operator++() { ++sub_iter_; - window_.get().pop_front(); - window_.get().push_back(sub_iter_); + window_->get().pop_front(); + window_->get().push_back(sub_iter_); return *this; } diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index 4f5acc02..a9f45e24 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -169,6 +169,18 @@ TEST_CASE("zip(filter(chunked()))", "[filter][chunked][zip]") { } } +TEST_CASE("zip(filter(sliding_window()))", "[filter][sliding_window][zip]") { + using iter::filter; + using iter::sliding_window; + using iter::zip; + std::vector v(15); + auto windows = sliding_window(v, 10); + auto filtered = filter([](auto&) { return true; }, windows); + for (auto&& [window] : zip(filtered)) { + REQUIRE(window.size() == 10); + } +} + TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", "[chain.from_iterable][imap]") { using iter::chain; From a06b18af7a1ffb6a3db7c650070b1fa7c68cc9e0 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 01:37:29 -0400 Subject: [PATCH 010/102] Tests imap(filter(groupby())) from #59 --- test/test_mixed.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index a9f45e24..67ffa2ca 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -181,6 +181,22 @@ TEST_CASE("zip(filter(sliding_window()))", "[filter][sliding_window][zip]") { } } +TEST_CASE("imap(filter(groupby()))", "[filter][groupby][imap])") { + using iter::filter; + using iter::groupby; + using iter::imap; + + std::vector v{true, true, true, false, false, true, true}; + auto a = groupby(v, [](bool b) { return b; }); + auto b = filter([](auto& g) { return g.first; }, a); + auto c = imap( + [](auto& g) { return std::distance(g.second.begin(), g.second.end()); }, + b); + for (auto x : c) { + (void)x; + } +} + TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", "[chain.from_iterable][imap]") { using iter::chain; From 662f7116d95cb3d17b73b73014e2e85d7ed8695a Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 01:40:47 -0400 Subject: [PATCH 011/102] Lazily find first element of filter() I believe the right approach is to not bind any references to elements until one actually starts iterating. This way the iterator can be passed around before iteration starts without a dangling reference. This should actually fix the filter() issues of #57 and #59 --- filter.hpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/filter.hpp b/filter.hpp index 10a793c6..2a632be2 100644 --- a/filter.hpp +++ b/filter.hpp @@ -50,12 +50,14 @@ class iter::impl::Filtered { template friend class Iterator; using Holder = DerefHolder>; - IteratorWrapper sub_iter_; + mutable IteratorWrapper sub_iter_; IteratorWrapper sub_end_; - Holder item_; + mutable Holder item_; FilterFunc* filter_func_; - void inc_sub_iter() { + // All of these are marked const because the sub_iter_ is lazily + // initialized. The morality of this is questionable. + void inc_sub_iter() const { ++sub_iter_; if (sub_iter_ != sub_end_) { item_.reset(*sub_iter_); @@ -64,13 +66,20 @@ class iter::impl::Filtered { // increment until the iterator points to is true on the // predicate. Called by constructor and operator++ - void skip_failures() { + void skip_failures() const { while ( sub_iter_ != sub_end_ && !std::invoke(*filter_func_, item_.get())) { inc_sub_iter(); } } + void init_if_first_use() const { + if (!item_ && sub_iter_ != sub_end_) { + item_.reset(*sub_iter_); + skip_failures(); + } + } + public: using iterator_category = std::input_iterator_tag; using value_type = iterator_traits_deref; @@ -82,22 +91,20 @@ class iter::impl::Filtered { IteratorWrapper&& sub_end, FilterFunc& filter_func) : sub_iter_{std::move(sub_iter)}, sub_end_{std::move(sub_end)}, - filter_func_(&filter_func) { - if (sub_iter_ != sub_end_) { - item_.reset(*sub_iter_); - } - skip_failures(); - } + filter_func_(&filter_func) {} typename Holder::reference operator*() { + init_if_first_use(); return item_.get(); } typename Holder::pointer operator->() { + init_if_first_use(); return item_.get_ptr(); } Iterator& operator++() { + init_if_first_use(); inc_sub_iter(); skip_failures(); return *this; @@ -111,6 +118,8 @@ class iter::impl::Filtered { template bool operator!=(const Iterator& other) const { + init_if_first_use(); + other.init_if_first_use(); return sub_iter_ != other.sub_iter_; } From bacdcb2199314bc3c382190884e1d24976133c9f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:05:13 -0400 Subject: [PATCH 012/102] Adds imap(dropwhile(groupby())) like from #59 --- test/test_mixed.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index 67ffa2ca..ff364271 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -197,6 +197,22 @@ TEST_CASE("imap(filter(groupby()))", "[filter][groupby][imap])") { } } +TEST_CASE("imap(dropwhile(groupby()))", "[dropwhile][groupby][imap])") { + using iter::dropwhile; + using iter::groupby; + using iter::imap; + + std::vector v{false, false, true, true, false, true, true}; + auto a = groupby(v, [](bool b) { return b; }); + auto b = dropwhile([](auto& g) { return g.first; }, a); + auto c = imap( + [](auto& g) { return std::distance(g.second.begin(), g.second.end()); }, + b); + for (auto x : c) { + (void)x; + } +} + TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", "[chain.from_iterable][imap]") { using iter::chain; From 29b1e4799d45c419047a89d8d00e8a89bb8be2da Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:09:56 -0400 Subject: [PATCH 013/102] Lazily finds first element in dropwhile for #57 --- dropwhile.hpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dropwhile.hpp b/dropwhile.hpp index ca190471..dc1874b0 100644 --- a/dropwhile.hpp +++ b/dropwhile.hpp @@ -39,12 +39,13 @@ class iter::impl::Dropper { template friend class Iterator; using Holder = DerefHolder>; - IteratorWrapper sub_iter_; + mutable IteratorWrapper sub_iter_; IteratorWrapper sub_end_; - Holder item_; + mutable Holder item_; FilterFunc* filter_func_; - void inc_sub_iter() { + // see comments from filter about mutability + void inc_sub_iter() const { ++sub_iter_; if (sub_iter_ != sub_end_) { item_.reset(*sub_iter_); @@ -52,12 +53,19 @@ class iter::impl::Dropper { } // skip all values for which the predicate is true - void skip_passes() { + void skip_passes() const { while (sub_iter_ != sub_end_ && std::invoke(*filter_func_, item_.get())) { inc_sub_iter(); } } + void init_if_first_use() const { + if (!item_ && sub_iter_ != sub_end_) { + item_.reset(*sub_iter_); + skip_passes(); + } + } + public: using iterator_category = std::input_iterator_tag; using value_type = iterator_traits_deref; @@ -69,22 +77,20 @@ class iter::impl::Dropper { IteratorWrapper&& sub_end, FilterFunc& filter_func) : sub_iter_{std::move(sub_iter)}, sub_end_{std::move(sub_end)}, - filter_func_(&filter_func) { - if (sub_iter_ != sub_end_) { - item_.reset(*sub_iter_); - } - skip_passes(); - } + filter_func_(&filter_func) {} typename Holder::reference operator*() { + init_if_first_use(); return item_.get(); } typename Holder::pointer operator->() { + init_if_first_use(); return item_.get_ptr(); } Iterator& operator++() { + init_if_first_use(); inc_sub_iter(); return *this; } @@ -97,6 +103,8 @@ class iter::impl::Dropper { template bool operator!=(const Iterator& other) const { + init_if_first_use(); + other.init_if_first_use(); return sub_iter_ != other.sub_iter_; } From a430b460eaa385ca911106a23ef5ee1d06371b0e Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:18:40 -0400 Subject: [PATCH 014/102] Adds imap(takewhile(groupby())) test from #59 --- test/test_mixed.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index ff364271..1e4ee949 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -213,6 +213,22 @@ TEST_CASE("imap(dropwhile(groupby()))", "[dropwhile][groupby][imap])") { } } +TEST_CASE("imap(takewhile(groupby()))", "[takewhile][groupby][imap])") { + using iter::groupby; + using iter::imap; + using iter::takewhile; + + std::vector v{true, true, true, false, false}; + auto a = groupby(v, [](bool b) { return b; }); + auto b = takewhile([](auto& g) { return g.first; }, a); + auto c = imap( + [](auto& g) { return std::distance(g.second.begin(), g.second.end()); }, + b); + for (auto x : c) { + (void)x; + } +} + TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", "[chain.from_iterable][imap]") { using iter::chain; From fbd57d345c037f4c39720027b14901dc61f9436f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:21:19 -0400 Subject: [PATCH 015/102] Lazily checks first element under predicate for #57 --- takewhile.hpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/takewhile.hpp b/takewhile.hpp index 8e28fcf2..a04d86b2 100644 --- a/takewhile.hpp +++ b/takewhile.hpp @@ -40,11 +40,14 @@ class iter::impl::Taker { template friend class Iterator; using Holder = DerefHolder>; - IteratorWrapper sub_iter_; + // I want this mutable so I can use operator* reliably in the const + // context of init_if_first_use + mutable IteratorWrapper sub_iter_; IteratorWrapper sub_end_; - Holder item_; + mutable Holder item_; FilterFunc* filter_func_; + // see comments from filter about mutability void inc_sub_iter() { ++sub_iter_; if (sub_iter_ != sub_end_) { @@ -52,12 +55,19 @@ class iter::impl::Taker { } } - void check_current() { + void check_current() const { if (sub_iter_ != sub_end_ && !std::invoke(*filter_func_, item_.get())) { sub_iter_ = sub_end_; } } + void init_if_first_use() const { + if (!item_ && sub_iter_ != sub_end_) { + item_.reset(*sub_iter_); + check_current(); + } + } + public: using iterator_category = std::input_iterator_tag; using value_type = iterator_traits_deref; @@ -69,22 +79,20 @@ class iter::impl::Taker { IteratorWrapper&& sub_end, FilterFunc& filter_func) : sub_iter_{std::move(sub_iter)}, sub_end_{std::move(sub_end)}, - filter_func_(&filter_func) { - if (sub_iter_ != sub_end_) { - item_.reset(*sub_iter_); - } - check_current(); - } + filter_func_(&filter_func) {} typename Holder::reference operator*() { + init_if_first_use(); return item_.get(); } typename Holder::pointer operator->() { + init_if_first_use(); return item_.get_ptr(); } Iterator& operator++() { + init_if_first_use(); inc_sub_iter(); check_current(); return *this; @@ -98,6 +106,8 @@ class iter::impl::Taker { template bool operator!=(const Iterator& other) const { + init_if_first_use(); + other.init_if_first_use(); return sub_iter_ != other.sub_iter_; } From a166113a3de4f5609745f24e789a43eace914c8f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:28:31 -0400 Subject: [PATCH 016/102] Adds flipped == iterator compare for dropwhile --- test/test_dropwhile.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/test_dropwhile.cpp b/test/test_dropwhile.cpp index 55370092..2b85c607 100644 --- a/test/test_dropwhile.cpp +++ b/test/test_dropwhile.cpp @@ -105,7 +105,12 @@ TEST_CASE("dropwhile: skips all elements when all are true under predicate", "[dropwhile]") { Vec ns{3, 4, 5, 6}; auto d = dropwhile([](int i) { return i != 0; }, ns); - REQUIRE(std::begin(d) == std::end(d)); + SECTION("normal compare") { + REQUIRE(std::begin(d) == std::end(d)); + } + SECTION("reversed compare") { + REQUIRE(std::end(d) == std::begin(d)); + } } TEST_CASE("dropwhile: identity", "[dropwhile]") { @@ -119,7 +124,12 @@ TEST_CASE("dropwhile: identity", "[dropwhile]") { TEST_CASE("dropwhile: empty case is empty", "[dropwhile]") { Vec ns{}; auto d = dropwhile([](int i) { return i != 0; }, ns); - REQUIRE(std::begin(d) == std::end(d)); + SECTION("normal compare") { + REQUIRE(std::begin(d) == std::end(d)); + } + SECTION("reversed compare") { + REQUIRE(std::end(d) == std::begin(d)); + } } TEST_CASE("dropwhile: only drops from beginning", "[dropwhile]") { From 7712c2abce1a486e5ea70eb655ec8b918fb24769 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:29:18 -0400 Subject: [PATCH 017/102] Adds flipped == iterator compare for filter --- test/test_filter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/test_filter.cpp b/test/test_filter.cpp index 73f684ee..b10dd5be 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -149,7 +149,12 @@ TEST_CASE("filter: all elements fail predicate", "[filter]") { Vec ns{10, 20, 30, 40, 50}; auto f = filter(less_than_five, ns); - REQUIRE(std::begin(f) == std::end(f)); + SECTION("normal compare") { + REQUIRE(std::begin(f) == std::end(f)); + } + SECTION("reversed compare") { + REQUIRE(std::end(f) == std::begin(f)); + } } TEST_CASE("filter: doesn't move or copy elements of iterable", "[filter]") { From 357961341543f4453ce2e5c2a85f2aa92da2ac56 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Jun 2019 02:29:32 -0400 Subject: [PATCH 018/102] Adds flipped == iterator compare for takewhile --- test/test_takewhile.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/test_takewhile.cpp b/test/test_takewhile.cpp index 23ad8462..903f6a9f 100644 --- a/test/test_takewhile.cpp +++ b/test/test_takewhile.cpp @@ -125,7 +125,12 @@ TEST_CASE("takewhile: everything passes predicate", "[takewhile]") { TEST_CASE("takewhile: empty iterable is empty", "[takewhile]") { Vec ns{}; auto tw = takewhile(under_ten, ns); - REQUIRE(std::begin(tw) == std::end(tw)); + SECTION("normal compare") { + REQUIRE(std::begin(tw) == std::end(tw)); + } + SECTION("reversed compare") { + REQUIRE(std::end(tw) == std::begin(tw)); + } } TEST_CASE( @@ -134,13 +139,23 @@ TEST_CASE( SECTION("First element is only element") { Vec ns = {20}; auto tw = takewhile(under_ten, ns); - REQUIRE(std::begin(tw) == std::end(tw)); + SECTION("normal compare") { + REQUIRE(std::begin(tw) == std::end(tw)); + } + SECTION("reversed compare") { + REQUIRE(std::end(tw) == std::begin(tw)); + } } SECTION("First element followed by elements that pass") { Vec ns = {20, 1, 1}; auto tw = takewhile(under_ten, ns); - REQUIRE(std::begin(tw) == std::end(tw)); + SECTION("normal compare") { + REQUIRE(std::begin(tw) == std::end(tw)); + } + SECTION("reversed compare") { + REQUIRE(std::end(tw) == std::begin(tw)); + } } } From dd34859b1cbd5fe8c7013fd0ab6888430fcaaef0 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 23 Jun 2019 12:32:41 -0400 Subject: [PATCH 019/102] Replaces count's min() call with lowest() Fixes #60 --- count.hpp | 3 +-- test/test_count.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/count.hpp b/count.hpp index 1694589c..327596bf 100644 --- a/count.hpp +++ b/count.hpp @@ -8,8 +8,7 @@ namespace iter { template constexpr auto count(T start, T step) noexcept { - // if step is < 0, set the stop to numeric min, otherwise numeric max - T stop = step < T(0) ? std::numeric_limits::min() + T stop = step < T(0) ? std::numeric_limits::lowest() : std::numeric_limits::max(); return range(start, stop, step); } diff --git a/test/test_count.cpp b/test/test_count.cpp index f0f0de68..8998f1eb 100644 --- a/test/test_count.cpp +++ b/test/test_count.cpp @@ -65,6 +65,68 @@ TEST_CASE("count: with step > 1", "[count]") { REQUIRE(v == vc); } +TEST_CASE("count: with unsigned", "[count]") { + constexpr unsigned int uint_max = + static_cast(std::numeric_limits::max()); + std::vector v{}; + int steps = 0; + for (auto i : count(uint_max)) { + v.push_back(i); + if (steps == 2) { + break; + } + ++steps; + } + const std::vector vc{uint_max, uint_max + 1, uint_max + 2}; + + REQUIRE(v == vc); +} + +TEST_CASE("count: with negative step", "[count]") { + std::vector v{}; + int steps = 0; + for (auto i : count(0, -1)) { + v.push_back(i); + if (steps == 2) { + break; + } + ++steps; + } + const std::vector vc{0, -1, -2}; + + REQUIRE(v == vc); +} + +TEST_CASE("count: with double", "[count]") { + std::vector v{}; + int steps = 0; + for (auto i : count(1.0, 0.5)) { + v.push_back(i); + if (steps == 3) { + break; + } + ++steps; + } + const std::vector vc{1.0, 1.5, 2.0, 2.5}; + + REQUIRE(v == vc); +} + +TEST_CASE("count: with negative double step", "[count]") { + std::vector v{}; + int steps = 0; + for (auto i : count(1.0, -0.5)) { + v.push_back(i); + if (steps == 3) { + break; + } + ++steps; + } + const std::vector vc{1.0, 0.5, 0.0, -0.5}; + + REQUIRE(v == vc); +} + TEST_CASE("count: can bo constexpr", "[count]") { constexpr auto c = count(); constexpr auto c2 = count(5); From 93d70efe2416d05ae74dde22e0fd0d60378798e0 Mon Sep 17 00:00:00 2001 From: botelho Date: Fri, 2 Aug 2019 11:50:24 -0700 Subject: [PATCH 020/102] First rev of batched --- BUILD | 1 + batched.hpp | 141 ++++++++++++++++++++++++++++++++++ examples/SConstruct | 1 + examples/batched_examples.cpp | 23 ++++++ itertools.hpp | 1 + test/BUILD | 1 + test/SConstruct | 1 + test/test_batched.cpp | 101 ++++++++++++++++++++++++ 8 files changed, 270 insertions(+) create mode 100644 batched.hpp create mode 100644 examples/batched_examples.cpp create mode 100644 test/test_batched.cpp diff --git a/BUILD b/BUILD index 8b162b86..bab14566 100644 --- a/BUILD +++ b/BUILD @@ -2,6 +2,7 @@ cc_library( name = "cppitertools", hdrs = [ "accumulate.hpp", + "batched.hpp", "chain.hpp", "chunked.hpp", "combinations.hpp", diff --git a/batched.hpp b/batched.hpp new file mode 100644 index 00000000..b57f0e28 --- /dev/null +++ b/batched.hpp @@ -0,0 +1,141 @@ +#ifndef ITER_BATCHED_HPP_ +#define ITER_BATCHED_HPP_ + +#include "internal/iterator_wrapper.hpp" +#include "internal/iteratoriterator.hpp" +#include "internal/iterbase.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace iter { + namespace impl { + template + class Batcher; + + using BatchedFn = IterToolFnBindSizeTSecond; + } + constexpr impl::BatchedFn batched{}; +} + +template +class iter::impl::Batcher { + private: + Container container_; + std::size_t num_batches_; + + Batcher(Container&& container, std::size_t const num_batches) + : container_(std::forward(container)), num_batches_{num_batches} {} + + friend BatchedFn; + + template + using IndexVector = std::vector>; + template + using DerefVec = IterIterWrapper>; + + public: + Batcher(Batcher&&) = default; + template + class Iterator { + private: + template + friend class Iterator; + std::shared_ptr> batch_ = + std::make_shared>(); + IteratorWrapper sub_iter_; + IteratorWrapper sub_end_; + std::size_t num_batches_; + std::size_t size_; + std::size_t count_; + + bool done() const { + return batch_->empty(); + } + + void refill_batch() { + batch_->get().clear(); + if (count_ < num_batches_) { + std::size_t const batch_size(size_ / num_batches_ + std::min(1, (size_ % num_batches_) / (count_ + 1))); + batch_->get().reserve(batch_size); + for (std::size_t i = 0; i < batch_size; ++i) { + batch_->get().push_back(sub_iter_); + ++sub_iter_; + } + ++count_; + } + } + + public: + using iterator_category = std::input_iterator_tag; + using value_type = DerefVec; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + Iterator(IteratorWrapper&& sub_iter, + IteratorWrapper&& sub_end, std::size_t num_batches) + : sub_iter_{std::move(sub_iter)}, + sub_end_{std::move(sub_end)}, + num_batches_{num_batches}, + size_{static_cast(std::distance(sub_iter_, sub_end_))}, + count_{0} { + refill_batch(); + } + + Iterator& operator++() { + refill_batch(); + return *this; + } + + Iterator operator++(int) { + auto ret = *this; + ++*this; + return ret; + } + + template + bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + template + bool operator==(const Iterator& other) const { + return done() == other.done() + && (done() || !(sub_iter_ != other.sub_iter_)); + } + + DerefVec& operator*() { + return *batch_; + } + + DerefVec* operator->() { + return batch_.get(); + } + }; + + Iterator begin() { + return {get_begin(container_), get_end(container_), num_batches_}; + } + + Iterator end() { + return {get_end(container_), get_end(container_), num_batches_}; + } + + Iterator> begin() const { + return {get_begin(std::as_const(container_)), + get_end(std::as_const(container_)), num_batches_}; + } + + Iterator> end() const { + return {get_end(std::as_const(container_)), + get_end(std::as_const(container_)), num_batches_}; + } +}; + +#endif diff --git a/examples/SConstruct b/examples/SConstruct index c1ed4d10..5f9110c9 100644 --- a/examples/SConstruct +++ b/examples/SConstruct @@ -15,6 +15,7 @@ env['ENV']['TERM'] = os.environ['TERM'] progs = Split( ''' accumulate + batched chain chunked combinatoric diff --git a/examples/batched_examples.cpp b/examples/batched_examples.cpp new file mode 100644 index 00000000..1d2d4a6c --- /dev/null +++ b/examples/batched_examples.cpp @@ -0,0 +1,23 @@ +#include + +#include +#include + +int main() { + std::vector v {1,2,3,4,5,6,7,8,9}; + std::cout << "num batches: 5\n"; + for (auto&& sec : iter::batched(v, 5)) { + for (auto&& i : sec) { + std::cout << i << " "; + } + std::cout << '\n'; + } + + std::cout << "num batches: 4\n"; + for (auto&& sec : iter::batched(v,4)) { + for (auto&& i : sec) { + std::cout << i << " "; + } + std::cout << '\n'; + } +} diff --git a/itertools.hpp b/itertools.hpp index 7dd9b9a8..bf824196 100644 --- a/itertools.hpp +++ b/itertools.hpp @@ -2,6 +2,7 @@ #define ITERTOOLS_ALL_HPP_ #include "accumulate.hpp" +#include "batched.hpp" #include "chain.hpp" #include "chunked.hpp" #include "combinations.hpp" diff --git a/test/BUILD b/test/BUILD index b7bdc149..9ceefecd 100644 --- a/test/BUILD +++ b/test/BUILD @@ -2,6 +2,7 @@ load(":make_tests.bzl", "itertools_tests") progs = [ "accumulate", + "batched", "chain", "chunked", "combinations", diff --git a/test/SConstruct b/test/SConstruct index e338b923..98af3051 100644 --- a/test/SConstruct +++ b/test/SConstruct @@ -23,6 +23,7 @@ if env['CXX'].startswith('clang++'): progs = Split( ''' accumulate + batched chain chunked combinations diff --git a/test/test_batched.cpp b/test/test_batched.cpp new file mode 100644 index 00000000..209edc42 --- /dev/null +++ b/test/test_batched.cpp @@ -0,0 +1,101 @@ +#include + +#include +#include +#include +#include + +#include "catch.hpp" +#include "helpers.hpp" + +using iter::batched; +using Vec = std::vector; +using ResVec = std::vector; + +TEST_CASE("batched: basic test", "[batched]") { + Vec ns = {1, 2, 3, 4, 5, 6}; + ResVec results; + SECTION("Normal call") { + for (auto&& g : batched(ns, 2)) { + results.emplace_back(std::begin(g), std::end(g)); + } + } + SECTION("Pipe") { + for (auto&& g : ns | batched(2)) { + results.emplace_back(std::begin(g), std::end(g)); + } + } + + ResVec rc = {{1, 2, 3}, {4 ,5, 6}}; + + REQUIRE(results == rc); +} + +TEST_CASE("batched: const batched", "[batched][const]") { + Vec ns = {1, 2, 3, 4, 5, 6}; + ResVec results; + SECTION("Normal call") { + const auto& ch = batched(ns, 2); + for (auto&& g : ch) { + results.emplace_back(std::begin(g), std::end(g)); + } + } + ResVec rc = {{1, 2, 3}, {4, 5, 6}}; + + REQUIRE(results == rc); +} + +TEST_CASE("batched: const iterators can be compared to non-const iterators", + "[batched][const]") { + auto c = batched(Vec{}, 1); + const auto& cc = c; + (void)(std::begin(c) == std::end(cc)); +} + +TEST_CASE("batched: len(iterable) % groupsize != 0", "[batched]") { + Vec ns = {1, 2, 3, 4, 5, 6, 7}; + ResVec results; + for (auto&& g : batched(ns, 3)) { + results.emplace_back(std::begin(g), std::end(g)); + } + + ResVec rc = {{1, 2, 3}, {4, 5}, {6, 7}}; + + REQUIRE(results == rc); +} + +TEST_CASE("batched: iterators can be compared", "[batched]") { + Vec ns = {1, 2, 3, 4, 5, 6, 7}; + auto g = batched(ns, 3); + auto it = std::begin(g); + REQUIRE(it == std::begin(g)); + REQUIRE_FALSE(it != std::begin(g)); + ++it; + REQUIRE(it != std::begin(g)); + REQUIRE_FALSE(it == std::begin(g)); +} + +TEST_CASE("batched: size 0 is empty", "[batched]") { + Vec ns{1, 2, 3}; + auto g = batched(ns, 0); + REQUIRE(std::begin(g) == std::end(g)); +} + +TEST_CASE("batched: empty iterable gives empty batched", "[batched]") { + Vec ns{}; + auto g = batched(ns, 1); + REQUIRE(std::begin(g) == std::end(g)); +} + +TEST_CASE("batched: iterator meets requirements", "[batched]") { + std::string s{}; + auto c = batched(s, 1); + REQUIRE(itertest::IsIterator::value); +} + +template +using ImpT = decltype(batched(std::declval(), 1)); +TEST_CASE("batched: has correct ctor and assign ops", "[batched]") { + REQUIRE(itertest::IsMoveConstructibleOnly>::value); + REQUIRE(itertest::IsMoveConstructibleOnly>::value); +} From 95b1d3881941c431aa389f24970db93abbee6976 Mon Sep 17 00:00:00 2001 From: botelho Date: Fri, 2 Aug 2019 15:39:18 -0700 Subject: [PATCH 021/102] Minor --- examples/batched_examples.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/batched_examples.cpp b/examples/batched_examples.cpp index 1d2d4a6c..dfe67486 100644 --- a/examples/batched_examples.cpp +++ b/examples/batched_examples.cpp @@ -14,7 +14,7 @@ int main() { } std::cout << "num batches: 4\n"; - for (auto&& sec : iter::batched(v,4)) { + for (auto&& sec : iter::batched(v, 4)) { for (auto&& i : sec) { std::cout << i << " "; } From 3e7ceca38cc17b2f6707da1615896628a47fecdb Mon Sep 17 00:00:00 2001 From: botelho Date: Fri, 2 Aug 2019 18:17:18 -0700 Subject: [PATCH 022/102] Added support for containers with different begin and end types --- batched.hpp | 24 +++++++++++++++++++++++- test/test_batched.cpp | 10 ++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/batched.hpp b/batched.hpp index b57f0e28..e5eadf13 100644 --- a/batched.hpp +++ b/batched.hpp @@ -78,12 +78,34 @@ class iter::impl::Batcher { using pointer = value_type*; using reference = value_type&; + template + struct distance_helper { + static constexpr difference_type distance(Iter1 it1, Iter2 it2) { + difference_type dist(0); + for (; it1 != it2; ++it1) + ++dist; + return dist; + } + }; + + template + struct distance_helper && std::is_arithmetic_v>> { + static constexpr difference_type distance(Iter1 it1, Iter2 it2) { + return std::distance(it1, it2); + } + }; + + template + difference_type distance(Iter1 it1, Iter2 it2) const { + return distance_helper::distance(it1, it2); + } + Iterator(IteratorWrapper&& sub_iter, IteratorWrapper&& sub_end, std::size_t num_batches) : sub_iter_{std::move(sub_iter)}, sub_end_{std::move(sub_end)}, num_batches_{num_batches}, - size_{static_cast(std::distance(sub_iter_, sub_end_))}, + size_{static_cast(distance(sub_iter_, sub_end_))}, count_{0} { refill_batch(); } diff --git a/test/test_batched.cpp b/test/test_batched.cpp index 209edc42..962b77ec 100644 --- a/test/test_batched.cpp +++ b/test/test_batched.cpp @@ -81,6 +81,16 @@ TEST_CASE("batched: size 0 is empty", "[batched]") { REQUIRE(std::begin(g) == std::end(g)); } +TEST_CASE("batched: Works with different begin and end types", "[batched]") { + CharRange cr{'f'}; + std::vector> results; + for (auto&& g : batched(cr, 3)) { + results.emplace_back(std::begin(g), std::end(g)); + } + std::vector> rc = {{'a', 'b'}, {'c', 'd'}, {'e'}}; + REQUIRE(results == rc); +} + TEST_CASE("batched: empty iterable gives empty batched", "[batched]") { Vec ns{}; auto g = batched(ns, 1); From 4ba6e0469b2868fc3cf8c430b060263847236c26 Mon Sep 17 00:00:00 2001 From: ssbotelh Date: Sat, 3 Aug 2019 10:54:50 -0700 Subject: [PATCH 023/102] Added batched to README.md --- README.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c85fd38..758b6491 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Status | Compilers [slice](#slice)
[sliding\_window](#sliding_window)
[chunked](#chunked)
+[batched](#batched)
##### Combinatoric fuctions [product](#product)
@@ -168,6 +169,7 @@ would expect them to behave: - accumulate - chain.from\_iterable - chunked +- batched - combinations - combinations\_with\_replacement - cycle @@ -753,7 +755,7 @@ for (auto&& sec : sliding_window(v,4)) { chunked ------ -chunked will yield subsequent chunkes of an iterable in blocks of a specified +chunked will yield subsequent chunks of an iterable in blocks of a specified size. The final chunk may be shorter than the rest if the chunk size given does not evenly divide the length of the iterable. @@ -774,6 +776,32 @@ The above prints: 5 6 7 8 9 ``` +batched +------- + +batched will yield a given number N of batches containing subsequent elements from an iterable, +assuming the iterable contains at least N elements. +The size of each batch is immaterial, but the implementation guarantees that no two batches will +differ in size by more than 1. + +Example usage: +```c++ +vector v {1,2,3,4,5,6,7,8,9}; +for (auto&& sec : batched(v,4)) { + for (auto&& i : sec) { + cout << i << ' '; + } + cout << '\n'; +} +``` + +The above prints: +``` +1 2 3 +4 5 +6 7 +8 9 +``` product ------ From 91d091fe884a6731d3c329a4c19b8e87552afc2f Mon Sep 17 00:00:00 2001 From: botelho Date: Sun, 4 Aug 2019 23:15:02 -0700 Subject: [PATCH 024/102] Added test case --- test/test_batched.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/test_batched.cpp b/test/test_batched.cpp index 962b77ec..e32adfc1 100644 --- a/test/test_batched.cpp +++ b/test/test_batched.cpp @@ -52,7 +52,7 @@ TEST_CASE("batched: const iterators can be compared to non-const iterators", (void)(std::begin(c) == std::end(cc)); } -TEST_CASE("batched: len(iterable) % groupsize != 0", "[batched]") { +TEST_CASE("batched: len(iterable) % num_batches != 0", "[batched]") { Vec ns = {1, 2, 3, 4, 5, 6, 7}; ResVec results; for (auto&& g : batched(ns, 3)) { @@ -64,6 +64,18 @@ TEST_CASE("batched: len(iterable) % groupsize != 0", "[batched]") { REQUIRE(results == rc); } +TEST_CASE("batched: num_batches > len(iterable)", "[batched]") { + Vec ns = {1, 2, 3, 4, 5, 6, 7}; + ResVec results; + for (auto&& g : batched(ns, 9)) { + results.emplace_back(std::begin(g), std::end(g)); + } + + ResVec rc = {{1}, {2}, {3}, {4}, {5}, {6}, {7}}; + + REQUIRE(results == rc); +} + TEST_CASE("batched: iterators can be compared", "[batched]") { Vec ns = {1, 2, 3, 4, 5, 6, 7}; auto g = batched(ns, 3); From 2a5de0c2b41f33dde724cb0442d272ea620078b4 Mon Sep 17 00:00:00 2001 From: botelho Date: Mon, 5 Aug 2019 09:20:41 -0700 Subject: [PATCH 025/102] Added another example --- examples/batched_examples.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/batched_examples.cpp b/examples/batched_examples.cpp index dfe67486..c63c05b3 100644 --- a/examples/batched_examples.cpp +++ b/examples/batched_examples.cpp @@ -20,4 +20,12 @@ int main() { } std::cout << '\n'; } + + std::cout << "num batches: 6\n"; + for (auto&& sec : iter::batched(v, 6)) { + for (auto&& i : sec) { + std::cout << i << " "; + } + std::cout << '\n'; + } } From c8b32d1ff8ec4273bed2d99ec32383529ce348c9 Mon Sep 17 00:00:00 2001 From: botelho Date: Mon, 5 Aug 2019 09:34:02 -0700 Subject: [PATCH 026/102] Added test for uneven batch sizes --- test/test_batched.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_batched.cpp b/test/test_batched.cpp index e32adfc1..bdd5e2a3 100644 --- a/test/test_batched.cpp +++ b/test/test_batched.cpp @@ -31,6 +31,18 @@ TEST_CASE("batched: basic test", "[batched]") { REQUIRE(results == rc); } +TEST_CASE("batched: uneven batch sizes", "[batched]") { + Vec ns = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + ResVec results; + for (auto&& g : batched(ns, 6)) { + results.emplace_back(std::begin(g), std::end(g)); + } + + ResVec rc = {{1, 2}, {3, 4}, {5, 6}, {7}, {8}, {9}}; + + REQUIRE(results == rc); +} + TEST_CASE("batched: const batched", "[batched][const]") { Vec ns = {1, 2, 3, 4, 5, 6}; ResVec results; From 41f7d221fe452dfc198914560355b837423e2a1d Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 25 Aug 2019 17:50:17 -0400 Subject: [PATCH 027/102] Replaces assignment with emplace in DerefHolder Fixes #62 --- internal/iterbase.hpp | 2 +- test/test_mixed.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/iterbase.hpp b/internal/iterbase.hpp index df1df2ba..57cc1b78 100644 --- a/internal/iterbase.hpp +++ b/internal/iterbase.hpp @@ -298,7 +298,7 @@ namespace iter { } void reset(T&& item) { - item_p_ = std::move(item); + item_p_.emplace(std::move(item)); } explicit operator bool() const { diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index 1e4ee949..e9b92d34 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -241,3 +241,18 @@ TEST_CASE("chain.from_iterable: accept imap result that yields rvalues", REQUIRE(v == vc); } + +TEST_CASE( + "filter(enumerate()), DerefHolder works correctly (see github issue #62", + "[filter][enumerate]") { + using iter::enumerate; + using iter::filter; + std::vector ns = {50, 55, 60, 65}; + auto f = + iter::filter([](auto& i) { return std::get<0>(i) > 1; }, enumerate(ns)); + const std::vector> v(std::begin(f), std::end(f)); + + const std::vector> vc = {{2, 60}, {3, 65}}; + + REQUIRE(v == vc); +} From 334c7155638a916244234678f3922bd063d68d95 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 16 Sep 2019 10:44:29 -0700 Subject: [PATCH 028/102] Adds MSVC 2019 to appveyor build status description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 758b6491..5f473218 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Follow [@cppitertools](https://twitter.com/cppitertools) for updates. Status | Compilers ---- | ---- [![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://travis-ci.com/ryanhaining/cppitertools) | gcc-7 gcc-8 clang-5.0 clang-6.0 -[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/ryanhaining/cppitertools?svg=true)](https://ci.appveyor.com/project/ryanhaining/cppitertools) | MSVC 2017 +[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/ryanhaining/cppitertools?svg=true)](https://ci.appveyor.com/project/ryanhaining/cppitertools) | MSVC 2017 MSVC 2019 #### Table of Contents [range](#range)
From cb3635456bdb531121b82b4d2e3afc7ae1f56d47 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Sun, 22 Dec 2019 21:29:36 -0500 Subject: [PATCH 029/102] Fixes const iteration for chain() Fixes #65 --- chain.hpp | 14 ++++++++++++-- test/test_chain.cpp | 14 +++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/chain.hpp b/chain.hpp index d289a6a3..23d1e054 100644 --- a/chain.hpp +++ b/chain.hpp @@ -25,6 +25,16 @@ namespace iter { // rather than a chain function, use a callable object to support // from_iterable class ChainMaker; + + template + struct AsTupleOfConstImpl; + + template + struct AsTupleOfConstImpl> + : type_is...>> {}; + + template + using AsTupleOfConst = typename AsTupleOfConstImpl::type; } } @@ -169,12 +179,12 @@ class iter::impl::Chained { {get_end(std::get(tup_))...}}; } - Iterator> begin() const { + Iterator> begin() const { return {0, {get_begin(std::as_const(std::get(tup_)))...}, {get_end(std::as_const(std::get(tup_)))...}}; } - Iterator> end() const { + Iterator> end() const { return {sizeof...(Is), {get_end(std::as_const(std::get(tup_)))...}, {get_end(std::as_const(std::get(tup_)))...}}; } diff --git a/test/test_chain.cpp b/test/test_chain.cpp index 6caabe3f..b4b77cf2 100644 --- a/test/test_chain.cpp +++ b/test/test_chain.cpp @@ -10,8 +10,8 @@ #include "catch.hpp" using iter::chain; -using itertest::SolidInt; using itertest::BasicIterable; +using itertest::SolidInt; using Vec = const std::vector; TEST_CASE("chain: three strings", "[chain]") { @@ -28,8 +28,8 @@ TEST_CASE("chain: three strings", "[chain]") { TEST_CASE("chain: const iteration", "[chain][const]") { std::string s1{"abc"}; - /* const */ std::string s2{"mno"}; - auto ch = chain(s1, s2, std::string{"xyz"}); + const std::string s2{"mno"}; + const auto ch = chain(s1, s2, std::string{"xyz"}); Vec v(std::begin(ch), std::end(ch)); Vec vc{'a', 'b', 'c', 'm', 'n', 'o', 'x', 'y', 'z'}; @@ -309,8 +309,8 @@ template using ImpT2 = decltype(chain.from_iterable(std::declval())); TEST_CASE("chain.from_iterable: has correct ctor and assign ops", "[chain.from_iterable]") { - REQUIRE(itertest::IsMoveConstructibleOnly>>:: - value); - REQUIRE(itertest::IsMoveConstructibleOnly&>>:: - value); + REQUIRE(itertest::IsMoveConstructibleOnly< + ImpT2>>::value); + REQUIRE(itertest::IsMoveConstructibleOnly< + ImpT2&>>::value); } From 2cc82d2be666aabd66279a8693fd2c6f196117c1 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 26 Dec 2019 15:04:25 -0800 Subject: [PATCH 030/102] Adds information to imap return type. Fixes #66 --- imap.hpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/imap.hpp b/imap.hpp index f022e6d0..1e84faf0 100644 --- a/imap.hpp +++ b/imap.hpp @@ -1,17 +1,21 @@ #ifndef ITER_IMAP_H_ #define ITER_IMAP_H_ +#include + #include "starmap.hpp" #include "zip.hpp" -#include - namespace iter { namespace impl { struct IMapFn : PipeableAndBindFirst { template - decltype(auto) operator()( - MapFunc map_func, Containers&&... containers) const { + auto operator()(MapFunc map_func, Containers&&... containers) const + // explicitly specifying type here to allow more expressions that only + // care about the type, and don't need a valid implementation. + // See #66 + -> StarMapper(containers)...))> { return starmap(map_func, zip(std::forward(containers)...)); } using PipeableAndBindFirst::operator(); From 0ebfbd60a05fe6ac2b9c39759dd91cc2cf69c136 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 26 Dec 2019 15:08:45 -0800 Subject: [PATCH 031/102] Removes trailing whitespace in README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5f473218..bd52d172 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Follow [@cppitertools](https://twitter.com/cppitertools) for updates. #### Build and Test Status Status | Compilers ----- | ---- +---- | ---- [![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://travis-ci.com/ryanhaining/cppitertools) | gcc-7 gcc-8 clang-5.0 clang-6.0 [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/ryanhaining/cppitertools?svg=true)](https://ci.appveyor.com/project/ryanhaining/cppitertools) | MSVC 2017 MSVC 2019 @@ -253,11 +253,11 @@ recomputed at each step to avoid accumulating floating point inaccuracies slower but more accurate. `range` also supports the following operations: - - `.size()` to get the number of elements in the range (not enabled for - floating point ranges). + - `.size()` to get the number of elements in the range (not enabled for + floating point ranges). - Accessors for `.start()`, `.stop()`, and `.step()`. - Indexing. Given a range `r`, `r[n]` is the `n`th element in the range. - + enumerate --------- @@ -782,7 +782,7 @@ batched batched will yield a given number N of batches containing subsequent elements from an iterable, assuming the iterable contains at least N elements. The size of each batch is immaterial, but the implementation guarantees that no two batches will -differ in size by more than 1. +differ in size by more than 1. Example usage: ```c++ From 342e7523e35470ce93e032e37a41eba4893c45c6 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 1 Jan 2020 23:45:47 -0500 Subject: [PATCH 032/102] Travis: add gcc-9, clang-7,8,9, upgrade to xenial --- .travis.yml | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++--- README.md | 2 +- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d4d97a0d..349a41d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: cpp -dist: trusty +dist: xenial sudo: require matrix: @@ -10,7 +10,7 @@ matrix: apt: sources: - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-5.0 + - llvm-toolchain-xenial packages: - clang-5.0 - clang++-5.0 @@ -18,6 +18,7 @@ matrix: - gcc-8-base - libc6 - libgcc1 + - scons env: - COMPILER=clang++-5.0 - USE_LIBCXX=1 @@ -27,7 +28,7 @@ matrix: apt: sources: - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-6.0 + - llvm-toolchain-xenial packages: - clang-6.0 - clang++-6.0 @@ -35,9 +36,67 @@ matrix: - gcc-8-base - libc6 - libgcc1 + - scons env: - COMPILER=clang++-6.0 - USE_LIBCXX=1 + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-xenial-7 + packages: + - clang-7 + - clang++-7 + - valgrind + - gcc-8-base + - libc6 + - libgcc1 + - scons + env: + - COMPILER=clang++-7 + - USE_LIBCXX=1 + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-xenial-8 + packages: + - clang-8 + - clang++-8 + - valgrind + - gcc-8-base + - libc6 + - libgcc1 + - scons + env: + - COMPILER=clang++-8 + - USE_LIBCXX=1 + - os: linux + compiler: clang + addons: + apt: + sources: + - ubuntu-toolchain-r-test + # travis complains about an unlisted source here if I do it with a plain + # llvm-toolchain-xenial-9 + - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - clang-9 + - clang++-9 + - valgrind + - gcc-8-base + - libc6 + - libgcc1 + - scons + env: + - COMPILER=clang++-9 + - USE_LIBCXX=1 - os: linux addons: apt: @@ -46,6 +105,7 @@ matrix: packages: - g++-7 - valgrind + - scons env: - COMPILER=g++-7 - os: linux @@ -56,8 +116,20 @@ matrix: packages: - g++-8 - valgrind + - scons env: - COMPILER=g++-8 + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + - valgrind + - scons + env: + - COMPILER=g++-9 script: - ${COMPILER} --version diff --git a/README.md b/README.md index bd52d172..a7f6e5ca 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Follow [@cppitertools](https://twitter.com/cppitertools) for updates. #### Build and Test Status Status | Compilers ---- | ---- -[![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://travis-ci.com/ryanhaining/cppitertools) | gcc-7 gcc-8 clang-5.0 clang-6.0 +[![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://travis-ci.com/ryanhaining/cppitertools) | gcc-7 gcc-8 gcc-9 clang-5.0 clang-6.0 clang-7 clang-8 clang-9 [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/ryanhaining/cppitertools?svg=true)](https://ci.appveyor.com/project/ryanhaining/cppitertools) | MSVC 2017 MSVC 2019 #### Table of Contents From 96ee0635e338a895502fe74ec1df7103ebc3e49e Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Fri, 17 Jan 2020 11:53:01 +0100 Subject: [PATCH 033/102] added conan build --- CMakeLists.txt | 41 +++++++++++++++++++++++++++++++++++++ cmake/dummy-config.cmake.in | 5 +++++ conanfile.py | 31 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/dummy-config.cmake.in create mode 100644 conanfile.py diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..59c84944 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.12) +project(cppitertools VERSION 2.0) +set(CMAKE_CXX_STANDARD 17) + + +# installation directories +set(cppitertools_INSTALL_INCLUDE_DIR "include" CACHE STRING "The installation include directory") +set(cppitertools_INSTALL_CMAKE_DIR "share/cppitertools/cmake" CACHE STRING "The installation cmake directory") + + +# define a header-only library +add_library(cppitertools INTERFACE) +add_library(cppitertools::cppitertools ALIAS cppitertools) + +target_include_directories(cppitertools INTERFACE + $ + $ + ) + + +# require C++17 +target_compile_features(cppitertools INTERFACE cxx_std_17) + +# Make package findable +configure_file(cmake/dummy-config.cmake.in cppitertools-config.cmake @ONLY) + +# Enable version checks in find_package +include(CMakePackageConfigHelpers) +write_basic_package_version_file(cppitertools-config-version.cmake COMPATIBILITY SameMajorVersion) + +# install and export target +install(TARGETS cppitertools EXPORT cppitertools-targets) + +install(EXPORT cppitertools-targets + FILE cppitertools-config.cmake + NAMESPACE cppitertools:: + DESTINATION ${cppitertools_INSTALL_CMAKE_DIR} + ) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppitertools-config-version.cmake DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}) +install(DIRECTORY . DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}) diff --git a/cmake/dummy-config.cmake.in b/cmake/dummy-config.cmake.in new file mode 100644 index 00000000..d8c788f1 --- /dev/null +++ b/cmake/dummy-config.cmake.in @@ -0,0 +1,5 @@ +# Dummy config file +# When a dependency is added with add_subdirectory, but searched with find_package + +# Redirect to the directory added with add_subdirectory +add_subdirectory(@PROJECT_SOURCE_DIR@ @PROJECT_BINARY_DIR@) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 00000000..4427b7a8 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,31 @@ +from conans import ConanFile, CMake + +import os + + +class Tentris_Parser(ConanFile): + name = "cppitertools" + version = "1.0" + author = "Ryan Haining" + description = "Implementation of python itertools and builtin iteration functions for C++17 " + topics = ("please add topics here") + settings = "build_type", "compiler", "os", "arch" + generators = "cmake", "cmake_find_package", "cmake_paths" + + exports_sources = list() + for file in os.listdir("."): + if file.endswith(".hpp"): + exports_sources.append(str(file)) + print("found files: " + str(exports_sources)) + + exports_sources = tuple(exports_sources) + \ + ("internal/*", "CMakeLists.txt", "cmake/dummy-config.cmake.in", "LICENSE.md") + no_copy_source = True + + def package(self): + cmake = CMake(self) + cmake.configure() + cmake.install() + + def package_id(self): + self.info.header_only() From d716cf6c8281ab6383d1fbecb456e0b9d808694c Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Fri, 17 Jan 2020 14:08:21 +0100 Subject: [PATCH 034/102] make the headers available at cppitertools/* --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59c84944..3caa4114 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(cppitertools::cppitertools ALIAS cppitertools) target_include_directories(cppitertools INTERFACE $ - $ + $ ) @@ -34,8 +34,8 @@ install(TARGETS cppitertools EXPORT cppitertools-targets) install(EXPORT cppitertools-targets FILE cppitertools-config.cmake NAMESPACE cppitertools:: - DESTINATION ${cppitertools_INSTALL_CMAKE_DIR} + DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}/cppitertools ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppitertools-config-version.cmake DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}) -install(DIRECTORY . DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}) +install(DIRECTORY . DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}/cppitertools) From 2547739b3ca1c1f08071b4c85df57f160df1f111 Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Fri, 17 Jan 2020 14:24:46 +0100 Subject: [PATCH 035/102] updated conan meta data --- conanfile.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/conanfile.py b/conanfile.py index 4427b7a8..b47cbab4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,23 +3,27 @@ import os -class Tentris_Parser(ConanFile): +class CppIterTools(ConanFile): name = "cppitertools" - version = "1.0" - author = "Ryan Haining" - description = "Implementation of python itertools and builtin iteration functions for C++17 " - topics = ("please add topics here") + version = "2.0" + author = "Ryan Haining " + homepage = "https://github.com/ryanhaining/cppitertools" + url = homepage + topics = ("conan", "itertools", "cppitertools") + license = 'BSD 2-Clause "Simplified" License' + description = "Range-based for loop add-ons inspired by the Python builtins and itertools library. " \ + "Like itertools and the Python3 builtins, this library uses lazy evaluation wherever possible." settings = "build_type", "compiler", "os", "arch" generators = "cmake", "cmake_find_package", "cmake_paths" + exports = "LICENSE.md" exports_sources = list() for file in os.listdir("."): if file.endswith(".hpp"): exports_sources.append(str(file)) print("found files: " + str(exports_sources)) - exports_sources = tuple(exports_sources) + \ - ("internal/*", "CMakeLists.txt", "cmake/dummy-config.cmake.in", "LICENSE.md") + ("internal/*", "CMakeLists.txt", "cmake/dummy-config.cmake.in") no_copy_source = True def package(self): From d245e3be3504a00acfe71fbbf153338519ea448c Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 20 Apr 2020 16:42:03 -0700 Subject: [PATCH 036/102] Fix CMakeLists.txt install paths The old code defaulted the cmake config install paths to share/cppitertools/cmake/cppitertools whereas CMake expects the config install directory to be one of (simplifying): share/cmake/cppitertools share/cppitertools share/cppitertools/cmake Unfortunately, the existing code chose to put `cppitertools` at the end of the install path unconditionally, and so we're left with either `share/cppitertools`, or `share/cmake/cppitertools` as options. Since other projects seemed to choose `share/${PROJECT_NAME}`, I figured that was a fine option, as long as the default is not broken. Therefore, this changes the default to share/cppitertools Additionally, the old code didn't unconditionally put `cppitertools` at the end of the config.version file, and so that was never being picked up. Since it was unlikely that anyone was depending on the config.version file being installed in the wrong location, we now install it at the same place as the config file. --- CMakeLists.txt | 52 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3caa4114..2bd4bc31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,34 @@ cmake_minimum_required(VERSION 3.12) project(cppitertools VERSION 2.0) -set(CMAKE_CXX_STANDARD 17) +if(NOT DEFINED ENV{cppitertools_INSTALL_CMAKE_DIR}) + message(WARNING [[ +The default value of cppitertools_INSTALL_CMAKE_DIR changed recently, from + "share/cppitertools/cmake" +to + "share" +in order to behave better with existing CMake practice. + +In order to get the previous behavior, pass + -Dcppitertools_INSTALL_CMAKE_DIR=share/cppitertools/cmake +to the CMake invocation; in order to get the new behavior without the warning, +pass + -Dcppitertools_INSTALL_CMAKE_DIR=share +explicitly. +]]) +endif() # installation directories set(cppitertools_INSTALL_INCLUDE_DIR "include" CACHE STRING "The installation include directory") -set(cppitertools_INSTALL_CMAKE_DIR "share/cppitertools/cmake" CACHE STRING "The installation cmake directory") - +set(cppitertools_INSTALL_CMAKE_DIR "share" CACHE STRING "The installation cmake directory") # define a header-only library add_library(cppitertools INTERFACE) add_library(cppitertools::cppitertools ALIAS cppitertools) target_include_directories(cppitertools INTERFACE - $ - $ - ) - + $ + $) # require C++17 target_compile_features(cppitertools INTERFACE cxx_std_17) @@ -29,13 +41,19 @@ include(CMakePackageConfigHelpers) write_basic_package_version_file(cppitertools-config-version.cmake COMPATIBILITY SameMajorVersion) # install and export target -install(TARGETS cppitertools EXPORT cppitertools-targets) - -install(EXPORT cppitertools-targets - FILE cppitertools-config.cmake - NAMESPACE cppitertools:: - DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}/cppitertools - ) - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppitertools-config-version.cmake DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}) -install(DIRECTORY . DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}/cppitertools) +install( + TARGETS cppitertools + EXPORT cppitertools-targets) + +install( + DIRECTORY . + DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}/cppitertools) + +install( + EXPORT cppitertools-targets + FILE cppitertools-config.cmake + NAMESPACE cppitertools:: + DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}/cppitertools) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/cppitertools-config-version.cmake + DESTINATION ${cppitertools_INSTALL_CMAKE_DIR}/cppitertools) From dc63b373020289012756b37f203d5cc75e355b2f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 8 Sep 2021 20:34:14 -0700 Subject: [PATCH 037/102] Replaces class with struct on tuple_(size|element) -Wmismatched-tags warns because the standard tuple_size and tuple_element are declared as struct. Fixes #82 --- enumerate.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enumerate.hpp b/enumerate.hpp index 6580b783..e2dda12b 100644 --- a/enumerate.hpp +++ b/enumerate.hpp @@ -38,11 +38,11 @@ namespace iter { namespace std { template - class tuple_size> + struct tuple_size> : public tuple_size> {}; template - class tuple_element> + struct tuple_element> : public tuple_element> {}; } From d4611ebd9664df7d92be4407d7c1c4675cd1ecd6 Mon Sep 17 00:00:00 2001 From: Patrick Fasano Date: Thu, 9 Sep 2021 04:08:30 -0400 Subject: [PATCH 038/102] cmake: Fix warning about cppitertools_INSTALL_CMAKE_DIR Fixes #76. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bd4bc31..f4f7eaeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12) project(cppitertools VERSION 2.0) -if(NOT DEFINED ENV{cppitertools_INSTALL_CMAKE_DIR}) +if(NOT DEFINED CACHE{cppitertools_INSTALL_CMAKE_DIR}) message(WARNING [[ The default value of cppitertools_INSTALL_CMAKE_DIR changed recently, from "share/cppitertools/cmake" From b338df3a17276f1458236a2715a7f2cd2cae9619 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Tue, 29 Mar 2022 11:18:49 -0700 Subject: [PATCH 039/102] Adds rbegin() and rend() to repeat Fixes #86 --- repeat.hpp | 8 ++++++++ test/test_mixed.cpp | 10 ++++++++++ test/test_repeat.cpp | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/repeat.hpp b/repeat.hpp index 01739c6c..7167c388 100644 --- a/repeat.hpp +++ b/repeat.hpp @@ -83,6 +83,14 @@ class iter::impl::RepeaterWithCount { constexpr Iterator end() const { return {&this->elem_, 0}; } + + constexpr Iterator rbegin() const { + return begin(); + } + + constexpr Iterator rend() const { + return end(); + } }; template diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index e9b92d34..e6b58bab 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -256,3 +256,13 @@ TEST_CASE( REQUIRE(v == vc); } + +TEST_CASE("reversed(repeat())", "[repeat][reversed]") { + using iter::reversed; + using iter::repeat; + + auto rr = reversed(repeat('x', 5)); + std::string s(rr.begin(), rr.end()); + + REQUIRE(s == "xxxxx"); +} diff --git a/test/test_repeat.cpp b/test/test_repeat.cpp index 0fc62e72..c972c4c2 100644 --- a/test/test_repeat.cpp +++ b/test/test_repeat.cpp @@ -56,6 +56,7 @@ TEST_CASE("repeat: iterators compare to const iterators", "[repeat]") { (void)(std::begin(r) == std::end(cr)); } + TEST_CASE("repeat: two argument repeats a number of times", "[repeat]") { auto r = repeat('a', 3); std::string s(std::begin(r), std::end(r)); @@ -86,6 +87,12 @@ TEST_CASE("repeat: iterator meets requirements", "[repeat]") { REQUIRE(itertest::IsIterator::value); } +TEST_CASE("repeat: is reversible", "[repeat]") { + auto r = repeat('b', 4); + std::string s(std::rbegin(r), std::rend(r)); + REQUIRE(s == "bbbb"); +} + template using ImpT = decltype(repeat(std::declval())); From fda93ac505bb29404f6704e2d616ec2cbc101395 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Tue, 29 Mar 2022 22:15:12 -0700 Subject: [PATCH 040/102] Adds rbegin() and rend() to two-argument repeat Fixes #86 --- repeat.hpp | 10 +++++++++- test/test_mixed.cpp | 21 +++++++++++++++------ test/test_repeat.cpp | 19 +++++++++++++------ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/repeat.hpp b/repeat.hpp index 7167c388..327c21b0 100644 --- a/repeat.hpp +++ b/repeat.hpp @@ -139,7 +139,7 @@ class iter::impl::Repeater { return *this; } - constexpr Iterator operator++(int)const { + constexpr Iterator operator++(int) const { return *this; } @@ -167,6 +167,14 @@ class iter::impl::Repeater { constexpr Iterator end() const { return {nullptr}; } + + constexpr Iterator rbegin() const { + return begin(); + } + + constexpr Iterator rend() const { + return end(); + } }; template diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index e6b58bab..9c4c3ed6 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -1,12 +1,11 @@ // mixing different itertools, there is nothing called iter::mixed() -#include "itertools.hpp" - -#include "catch.hpp" - #include #include +#include "catch.hpp" +#include "itertools.hpp" + class MyUnMovable { int val; @@ -257,12 +256,22 @@ TEST_CASE( REQUIRE(v == vc); } -TEST_CASE("reversed(repeat())", "[repeat][reversed]") { - using iter::reversed; +TEST_CASE("reversed(repeat(v, n))", "[repeat][reversed]") { using iter::repeat; + using iter::reversed; auto rr = reversed(repeat('x', 5)); std::string s(rr.begin(), rr.end()); REQUIRE(s == "xxxxx"); } + +TEST_CASE("reversed(repeat(v))", "[repeat][reversed]") { + using iter::repeat; + using iter::reversed; + + auto rr = reversed(repeat('x')); + auto it = rr.begin(); + + REQUIRE(*it == 'x'); +} diff --git a/test/test_repeat.cpp b/test/test_repeat.cpp index c972c4c2..ab92cbc1 100644 --- a/test/test_repeat.cpp +++ b/test/test_repeat.cpp @@ -1,12 +1,10 @@ -#include - -#include "helpers.hpp" - #include +#include #include #include #include "catch.hpp" +#include "helpers.hpp" using iter::repeat; @@ -56,7 +54,6 @@ TEST_CASE("repeat: iterators compare to const iterators", "[repeat]") { (void)(std::begin(r) == std::end(cr)); } - TEST_CASE("repeat: two argument repeats a number of times", "[repeat]") { auto r = repeat('a', 3); std::string s(std::begin(r), std::end(r)); @@ -87,7 +84,17 @@ TEST_CASE("repeat: iterator meets requirements", "[repeat]") { REQUIRE(itertest::IsIterator::value); } -TEST_CASE("repeat: is reversible", "[repeat]") { +TEST_CASE("repeat: one-argument is reversible", "[repeat]") { + auto r = repeat('c'); + auto it = std::rbegin(r); + (void)(it != std::rend(r)); + + REQUIRE(*it == 'c'); + ++it; + REQUIRE(*it == 'c'); +} + +TEST_CASE("repeat: two-argument is reversible", "[repeat]") { auto r = repeat('b', 4); std::string s(std::rbegin(r), std::rend(r)); REQUIRE(s == "bbbb"); From affe89e17d8121e3c26391b7e8a63de0e8e4a78d Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 19 Jul 2022 09:51:47 -0700 Subject: [PATCH 041/102] Removes note on (discouraged) .index and .element in enumerate --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7f6e5ca..107ca507 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,8 @@ enumerate --------- Continually "yields" containers similar to pairs. They are basic structs with a -.index and a .element, and also work with structured binding declarations. +an index in .first, and the element in .second, and also work with structured +binding declarations. Usage appears as: ```c++ From c703a135d6bec700a5e687b60e834d05f0915894 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 19 Jul 2022 09:52:57 -0700 Subject: [PATCH 042/102] fixes enumerate typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 107ca507..66f48aae 100644 --- a/README.md +++ b/README.md @@ -261,8 +261,8 @@ slower but more accurate. enumerate --------- -Continually "yields" containers similar to pairs. They are basic structs with a -an index in .first, and the element in .second, and also work with structured +Continually "yields" containers similar to pairs. They are structs with +the index in `.first`, and the element in `.second`, and also work with structured binding declarations. Usage appears as: From ad94c5a89a89bff8b2168b03a7d1442e2f768552 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Wed, 17 Aug 2022 14:08:35 -0700 Subject: [PATCH 043/102] Updates starmap ::reference to match * Updates the Iterator::reference type to match the type of operator*() --- starmap.hpp | 21 +++++++++++------ test/test_starmap.cpp | 53 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/starmap.hpp b/starmap.hpp index d3fb678b..fbeaad68 100644 --- a/starmap.hpp +++ b/starmap.hpp @@ -34,8 +34,10 @@ class iter::impl::StarMapper { mutable Func func_; Container container_; - using StarIterDeref = std::remove_reference_t>()))>; + using StarIterDeref = + decltype(std::apply(func_, std::declval>())); + using StarIterDerefValue = + std::remove_cv_t>; StarMapper(Func f, Container&& c) : func_(std::move(f)), container_(std::forward(c)) {} @@ -53,10 +55,10 @@ class iter::impl::StarMapper { public: using iterator_category = std::input_iterator_tag; - using value_type = StarIterDeref; + using value_type = StarIterDerefValue; using difference_type = std::ptrdiff_t; using pointer = value_type*; - using reference = value_type&; + using reference = StarIterDeref; Iterator(Func& f, IteratorWrapper&& sub_iter) : func_(&f), sub_iter_(std::move(sub_iter)) {} @@ -86,7 +88,7 @@ class iter::impl::StarMapper { return std::apply(*func_, *sub_iter_); } - auto operator-> () -> ArrowProxy { + auto operator->() -> ArrowProxy { return {**this}; } }; @@ -130,7 +132,12 @@ class iter::impl::TupleStarMapper { class IteratorData { public: template - static auto get_and_call_with_tuple(Func& f, TupTypeT& t) -> decltype(std::apply(f, std::get(t))) { //TODO: Remove duplicated expression in decltype, using decltype(auto) as return type, when all compilers correctly deduce type (i.e. MSVC cl 19.15 does not do it). + static auto get_and_call_with_tuple(Func& f, TupTypeT& t) + -> decltype(std::apply(f, + std::get(t))) { // TODO: Remove duplicated expression in + // decltype, using decltype(auto) as return + // type, when all compilers correctly deduce + // type (i.e. MSVC cl 19.15 does not do it). return std::apply(f, std::get(t)); } @@ -169,7 +176,7 @@ class iter::impl::TupleStarMapper { return IteratorData::callers[index_](*func_, *tup_); } - auto operator-> () { + auto operator->() { return ArrowProxy{**this}; } diff --git a/test/test_starmap.cpp b/test/test_starmap.cpp index 4c12b7c1..528d5f0f 100644 --- a/test/test_starmap.cpp +++ b/test/test_starmap.cpp @@ -1,10 +1,10 @@ #include - #include "helpers.hpp" #include #include #include +#include #include #include "catch.hpp" @@ -16,6 +16,18 @@ namespace { return d * i; } + int& larger_ref(int& a, int& b) { + return a > b ? a : b; + } + + const int& larger_const_ref(const int& a, const int& b) { + return a > b ? a : b; + } + + int larger(int a, int b) { + return a > b ? a : b; + } + std::string g(const std::string& s, int i, char c) { std::stringstream ss; ss << s << ' ' << i << ' ' << c; @@ -74,7 +86,8 @@ TEST_CASE("starmap: works with pointer to member function", "[starmap]") { TEST_CASE("starmap: vector of pairs const iteration", "[starmap][const]") { using Vec = const std::vector; - const std::vector> v1 = {{1.0, 2}, {3.0, 11}, {6.0, 7}}; + const std::vector> v1 = { + {1.0, 2}, {3.0, 11}, {6.0, 7}}; const auto sm = starmap(Callable{}, v1); std::vector v(std::begin(sm), std::end(sm)); @@ -182,3 +195,39 @@ TEST_CASE( auto sm = starmap(Callable{}, tup); REQUIRE(itertest::IsIterator::value); } + +TEST_CASE("starmap: iterator dereference type matches 'reference' type alias", + "[starmap]") { + std::vector> input; + SECTION("with reference return type") { + auto sm = iter::starmap(larger_ref, input); + REQUIRE( + std::is_same_v); + } + SECTION("with const reference return type") { + auto sm = iter::starmap(larger_const_ref, input); + REQUIRE( + std::is_same_v); + } + SECTION("with value return type") { + auto sm = iter::starmap(larger, input); + REQUIRE( + std::is_same_v); + } +} + +TEST_CASE("starmap: iterator has correct 'value' type alias", "[starmap]") { + std::vector> input; + SECTION("with reference return type") { + auto sm = iter::starmap(larger_ref, input); + REQUIRE(std::is_same_v); + } + SECTION("with const reference return type") { + auto sm = iter::starmap(larger_const_ref, input); + REQUIRE(std::is_same_v); + } + SECTION("with value return type") { + auto sm = iter::starmap(larger, input); + REQUIRE(std::is_same_v); + } +} From e4fb18534092ae001f4af47b5a3420beba4c4390 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:23:55 -0700 Subject: [PATCH 044/102] Adds test helper for reference correctness operator* needs to match Iterator::reference Issue #88 --- test/helpers.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/helpers.hpp b/test/helpers.hpp index aa80cd3c..93112519 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -2,7 +2,6 @@ #define TEST_HELPER_H_ #include - #include #include #include @@ -208,6 +207,10 @@ namespace itertest { template struct IsIterator : std::false_type {}; + template + struct ReferenceMatchesDeref + : std::is_same())> {}; + template struct IsIterator())), // copyctor @@ -216,8 +219,8 @@ namespace itertest { decltype(std::declval().operator->()), // operator-> decltype(++std::declval()), // prefix ++ decltype(std::declval()++), // postfix ++ - decltype( - std::declval() != std::declval()), // != + decltype(std::declval() + != std::declval()), // != decltype(std::declval() == std::declval()) // == >> : std::true_type {}; From d70c2b1a7f478ce1d6d6eddfe49ea273c0e34c71 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:31:40 -0700 Subject: [PATCH 045/102] Uses ReferenceMatchesDeref in starmap test Issue #88 --- test/test_starmap.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/test_starmap.cpp b/test/test_starmap.cpp index 528d5f0f..feae8da9 100644 --- a/test/test_starmap.cpp +++ b/test/test_starmap.cpp @@ -201,18 +201,15 @@ TEST_CASE("starmap: iterator dereference type matches 'reference' type alias", std::vector> input; SECTION("with reference return type") { auto sm = iter::starmap(larger_ref, input); - REQUIRE( - std::is_same_v); + REQUIRE(itertest::ReferenceMatchesDeref::value); } SECTION("with const reference return type") { auto sm = iter::starmap(larger_const_ref, input); - REQUIRE( - std::is_same_v); + REQUIRE(itertest::ReferenceMatchesDeref::value); } SECTION("with value return type") { auto sm = iter::starmap(larger, input); - REQUIRE( - std::is_same_v); + REQUIRE(itertest::ReferenceMatchesDeref::value); } } From 5e8ddb3f13a2f1974a3b01137f68d4e4896a1ede Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:34:45 -0700 Subject: [PATCH 046/102] Fixes reference type in accumulate Issue #88 --- accumulate.hpp | 13 +++++++------ test/test_accumulate.cpp | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/accumulate.hpp b/accumulate.hpp index 0d43a20e..94c0c369 100644 --- a/accumulate.hpp +++ b/accumulate.hpp @@ -28,8 +28,9 @@ class iter::impl::Accumulator { friend AccumulateFn; - using AccumVal = std::remove_reference_t, iterator_deref>>; + using AccumVal = std::remove_cv_t< + std::remove_reference_t, iterator_deref>>>; Accumulator(Container&& container, AccumulateFunc accumulate_func) : container_(std::forward(container)), @@ -52,8 +53,8 @@ class iter::impl::Accumulator { using iterator_category = std::input_iterator_tag; using value_type = AccumVal; using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; + using pointer = const value_type*; + using reference = const value_type&; Iterator(IteratorWrapper&& sub_iter, IteratorWrapper&& sub_end, AccumulateFunc& accumulate_fun) @@ -65,11 +66,11 @@ class iter::impl::Accumulator { ? std::nullopt : std::make_optional(*sub_iter_)} {} - const AccumVal& operator*() const { + reference operator*() const { return *acc_val_; } - const AccumVal* operator->() const { + pointer operator->() const { return &*acc_val_; } diff --git a/test/test_accumulate.cpp b/test/test_accumulate.cpp index 02467fb2..f91f32b6 100644 --- a/test/test_accumulate.cpp +++ b/test/test_accumulate.cpp @@ -131,11 +131,24 @@ TEST_CASE("accumulate: operator->", "[accumulate]") { } TEST_CASE("accumulate: iterator meets requirements", "[accumulate]") { - Vec ns{}; - auto a = accumulate(ns, [](int a, int b) { return a + b; }); - auto it = std::begin(a); - it = std::begin(a); - REQUIRE(itertest::IsIterator::value); + std::vector ns{}; + SECTION("with reference return type") { + auto acc = accumulate(ns, [](int& a, int&) -> int& { return a; }); + REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); + } + + SECTION("with const reference return type") { + auto acc = accumulate(ns, [](int& a, int&) -> const int& { return a; }); + REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); + } + + SECTION("with value return type") { + auto acc = accumulate(ns, [](int a, int) -> int { return a; }); + REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); + } } TEST_CASE( From 038a4363a1bc8c9af4edc73b0d825256241aa2d8 Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:35:44 -0700 Subject: [PATCH 047/102] Fixes reference type in product Issue #88 --- product.hpp | 4 ++-- test/test_product.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/product.hpp b/product.hpp index d7bc61e9..7de5727a 100644 --- a/product.hpp +++ b/product.hpp @@ -98,7 +98,7 @@ class iter::impl::Productor { using value_type = TupleDeref; using difference_type = std::ptrdiff_t; using pointer = value_type*; - using reference = value_type&; + using reference = value_type; IteratorTempl(IteratorTuple&& iters, IteratorTuple&& end_iters) @@ -148,7 +148,7 @@ class iter::impl::Productor { return {(*std::get(iters_))...}; } - auto operator-> () -> ArrowProxy { + auto operator->() -> ArrowProxy { return {**this}; } }; diff --git a/test/test_product.cpp b/test/test_product.cpp index aa7ed1bb..6a580bc9 100644 --- a/test/test_product.cpp +++ b/test/test_product.cpp @@ -221,6 +221,7 @@ TEST_CASE("product: iterator meets requirements", "[product]") { std::string s{"abc"}; auto c = product(s, s); REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); } template From 86e8a0cdd30a00152ff82633ace8e86386c9cd2c Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:37:26 -0700 Subject: [PATCH 048/102] Fixes reference type in zip Issue #88 --- test/test_zip.cpp | 2 ++ zip.hpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_zip.cpp b/test/test_zip.cpp index 7bf8ff96..cb49981c 100644 --- a/test/test_zip.cpp +++ b/test/test_zip.cpp @@ -140,8 +140,10 @@ TEST_CASE("zip: iterator meets requirements", "[zip]") { std::string s{}; auto c = zip(s); REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); auto c2 = zip(s, s); REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); } template diff --git a/zip.hpp b/zip.hpp index abadf45c..98bfd3c4 100644 --- a/zip.hpp +++ b/zip.hpp @@ -56,7 +56,7 @@ class iter::impl::Zipped { using value_type = TupleDeref; using difference_type = std::ptrdiff_t; using pointer = value_type*; - using reference = value_type&; + using reference = value_type; Iterator(IteratorTuple&& iters) : iters_(std::move(iters)) {} @@ -91,7 +91,7 @@ class iter::impl::Zipped { return {(*std::get(iters_))...}; } - auto operator-> () -> ArrowProxy { + auto operator->() -> ArrowProxy { return {**this}; } }; From cc5f11f9311d8670b50d52ccb08eb1c984f8294c Mon Sep 17 00:00:00 2001 From: ryanhaining Date: Thu, 18 Aug 2022 16:38:21 -0700 Subject: [PATCH 049/102] Fixes reference type in slice Issue #88 --- slice.hpp | 13 ++++++------- test/test_slice.cpp | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/slice.hpp b/slice.hpp index 22855645..a926ecbb 100644 --- a/slice.hpp +++ b/slice.hpp @@ -51,7 +51,7 @@ class iter::impl::Sliced { using value_type = iterator_traits_deref; using difference_type = std::ptrdiff_t; using pointer = value_type*; - using reference = value_type&; + using reference = iterator_deref; Iterator(IteratorWrapper&& sub_iter, IteratorWrapper&& sub_end, DifferenceType start, @@ -131,10 +131,9 @@ struct iter::impl::SliceFn { private: friend SliceFn; - constexpr FnPartial(DifferenceType start, DifferenceType stop, - DifferenceType step) noexcept : start_{start}, - stop_{stop}, - step_{step} {} + constexpr FnPartial( + DifferenceType start, DifferenceType stop, DifferenceType step) noexcept + : start_{start}, stop_{stop}, step_{step} {} DifferenceType start_; DifferenceType stop_; DifferenceType step_; @@ -159,8 +158,8 @@ struct iter::impl::SliceFn { template >> - constexpr FnPartial operator()(DifferenceType stop) const - noexcept { + constexpr FnPartial operator()( + DifferenceType stop) const noexcept { return {0, stop, 1}; } diff --git a/test/test_slice.cpp b/test/test_slice.cpp index 38386b73..5fae78f4 100644 --- a/test/test_slice.cpp +++ b/test/test_slice.cpp @@ -148,9 +148,18 @@ TEST_CASE("slice: with iterable doesn't move or copy elems", "[slice]") { } TEST_CASE("slice: iterator meets requirements", "[slice]") { - std::string s{"abcdef"}; - auto c = slice(s, 1, 3); - REQUIRE(itertest::IsIterator::value); + SECTION("with iterable yielding references") { + std::string s{"abcdef"}; + auto c = slice(s, 1, 3); + REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); + } + SECTION("with iterable yielding values") { + itertest::InputIterable it{}; + auto c = slice(it, 1, 3); + REQUIRE(itertest::IsIterator::value); + REQUIRE(itertest::ReferenceMatchesDeref::value); + } } template From 6f7a3d042da474126f519fd4e70d0db06e28a82c Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 26 Dec 2022 18:30:02 -0800 Subject: [PATCH 050/102] Updates catch.hpp version --- test/download_catch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/download_catch.sh b/test/download_catch.sh index 487e1233..4f1ed06b 100755 --- a/test/download_catch.sh +++ b/test/download_catch.sh @@ -1,2 +1,2 @@ #!/usr/bin/env sh -wget -c https://github.com/catchorg/Catch2/releases/download/v2.6.0/catch.hpp +wget -c https://github.com/catchorg/Catch2/releases/download/v2.13.10/catch.hpp From 09e472243bdaf81c11ebeb9806092037c35253f1 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 26 Dec 2022 18:36:21 -0800 Subject: [PATCH 051/102] Adds unique_everseen taking hash and eq functions Fixes #90 --- test/test_unique_everseen.cpp | 30 ++++++++++++++++++++++++++++++ unique_everseen.hpp | 32 ++++++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/test/test_unique_everseen.cpp b/test/test_unique_everseen.cpp index d70de075..68cb319a 100644 --- a/test/test_unique_everseen.cpp +++ b/test/test_unique_everseen.cpp @@ -85,3 +85,33 @@ TEST_CASE( REQUIRE(itertest::IsMoveConstructibleOnly>::value); REQUIRE(itertest::IsMoveConstructibleOnly>::value); } + +struct IntWrapper { + int n; +}; + +struct IntWrapperHash { + int operator()(const IntWrapper& iw) const { + return iw.n % 10; + } +}; + +struct IntWrapperEq { + int operator()(const IntWrapper& lhs, const IntWrapper& rhs) const { + return lhs.n == rhs.n; + } +}; + +TEST_CASE("unique_everseen: works with custom hash and equality functions", + "[unique_everseen]") { + std::vector iwv = { + {2}, {3}, {4}, {2}, {10}, {2}, {2}, {12}, {10}}; + Vec vc{2, 3, 4, 10, 12}; + + std::vector v; + for (auto&& iw : unique_everseen(iwv, IntWrapperHash{}, IntWrapperEq{})) { + v.push_back(iw.n); + } + + REQUIRE(v == vc); +} diff --git a/unique_everseen.hpp b/unique_everseen.hpp index 35322ca8..1e138d63 100644 --- a/unique_everseen.hpp +++ b/unique_everseen.hpp @@ -13,15 +13,35 @@ namespace iter { namespace impl { struct UniqueEverseenFn : Pipeable { + private: template - auto operator()(Container&& container) const { - using elem_type = impl::iterator_deref; - auto func = [elem_seen = std::unordered_set>()]( - const std::remove_reference_t& e) mutable { - return elem_seen.insert(e).second; - }; + using Key = std::decay_t>; + + public: + template + auto operator()(Container&& container, const Hash& hash, + const KeyEqual& key_equal) const { + // You can't pass a hash function or an equality function without + // passing a bucket_count as well. We get the default bucket count here + // the first time this function runs. + static auto default_bucket_count = + std::unordered_set{}.bucket_count(); + using elem_type = iterator_deref; + auto func = + [elem_seen = + std::unordered_set, Hash, KeyEqual>( + default_bucket_count, hash, key_equal)]( + const std::remove_reference_t& e) mutable { + return elem_seen.insert(e).second; + }; return filter(func, std::forward(container)); } + + template + auto operator()(Container&& container) const { + return (*this)(std::forward(container), + std::hash>{}, std::equal_to>{}); + } }; } From a788a37bcf4a1f996181e7639efbf506484c0d1e Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 26 Dec 2022 18:52:12 -0800 Subject: [PATCH 052/102] Describes unique_everseen with hash and eq functions Issue #90 --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 66f48aae..c405d760 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ would expect them to behave: - sorted - starmap - takewhile -- unique\_everseen +- unique\_everseen (\*only without custom hash and equality callables) - unique\_justseen I don't personally care for the piping style, but it seemed to be desired by @@ -323,7 +323,7 @@ unique\_everseen *Additional Requirements*: Underlying values must be copy-constructible. This is a filter adaptor that only generates values that have never been seen -before. For this to work your object must be specialized for `std::hash`. +before. Prints `1 2 3 4 5 6 7 8 9` ```c++ @@ -333,6 +333,18 @@ for (auto&& i : unique_everseen(v)) { } ``` +`unique_everseen` uses an `undordered_set` so it needs hashable elements. For +types that don't work with `std::hash` or `std::equal_to`, `unique_everseen` +also provides an overload taking a hash callable and an equality callable. +This **does not** work with the pipe syntax. + +```c++ +vector v { /* ... */ }; +for (auto&& w : unique_everseen(v, WidgetHash{}, WidgetEq{})) { + cout << w.name() << ' '; +} +``` + unique\_justseen -------------- Another filter adaptor that only omits consecutive duplicates. From 57e5494a22a06757059f8d6b12f41d80abe778b3 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 10:34:21 -0800 Subject: [PATCH 053/102] Moves Identity function from groupby to iterbase. Issue #90 --- groupby.hpp | 7 ------- internal/iterbase.hpp | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/groupby.hpp b/groupby.hpp index 4f2fffb5..cac82426 100644 --- a/groupby.hpp +++ b/groupby.hpp @@ -18,13 +18,6 @@ namespace iter { template class GroupProducer; - struct Identity { - template - const T& operator()(const T& t) const { - return t; - } - }; - using GroupByFn = IterToolFnOptionalBindSecond; } constexpr impl::GroupByFn groupby{}; diff --git a/internal/iterbase.hpp b/internal/iterbase.hpp index 57cc1b78..69db7599 100644 --- a/internal/iterbase.hpp +++ b/internal/iterbase.hpp @@ -129,6 +129,13 @@ namespace iter { template constexpr bool is_iterable = IsIterable::value; + struct Identity { + template + const T& operator()(const T& t) const { + return t; + } + }; + namespace detail { template struct ArrowHelper { From 22be440497442098015cab4d5403628df2e7e8fa Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 10:41:17 -0800 Subject: [PATCH 054/102] Adds overload taking KeyFunc for types without == Issue #90 --- test/test_unique_justseen.cpp | 24 ++++++++++++++++++++++++ unique_justseen.hpp | 16 ++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/test/test_unique_justseen.cpp b/test/test_unique_justseen.cpp index c6671e5f..455ac927 100644 --- a/test/test_unique_justseen.cpp +++ b/test/test_unique_justseen.cpp @@ -91,3 +91,27 @@ TEST_CASE( REQUIRE(itertest::IsMoveConstructibleOnly>::value); REQUIRE(itertest::IsMoveConstructibleOnly>::value); } + +struct IntWrapper { + int n; +}; + +struct IntWrapperKey { + int operator()(const IntWrapper& iw) const { + return iw.n; + } +}; + +TEST_CASE("unique_justseen: works with key function", + "[unique_justseen]") { + std::vector iwv = { + {2}, {3}, {4}, {2}, {10}, {2}, {2}, {12}, {10}}; + Vec vc{2, 3, 4, 2, 10, 2, 12, 10}; + + std::vector v; + for (auto&& iw : unique_justseen(iwv, IntWrapperKey{})) { + v.push_back(iw.n); + } + + REQUIRE(v == vc); +} diff --git a/unique_justseen.hpp b/unique_justseen.hpp index 2d63b7ea..b717392f 100644 --- a/unique_justseen.hpp +++ b/unique_justseen.hpp @@ -10,12 +10,20 @@ namespace iter { namespace impl { struct UniqueJustseenFn : Pipeable { + public: + template + auto operator()(Container&& container, KeyFunc key_fn) const { + // decltype(auto) return type in lambda so reference types are preserved + return imap( + [](auto&& group) -> decltype(auto) { + return *get_begin(group.second); + }, + groupby(std::forward(container), key_fn)); + } + template auto operator()(Container&& container) const { - // decltype(auto) return type in lambda so reference types are preserved - return imap([](auto&& group) -> decltype( - auto) { return *get_begin(group.second); }, - groupby(std::forward(container))); + return (*this)(std::forward(container), Identity{}); } }; } From 2584f419309e785290036666036e34e73c64690f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 11:27:32 -0800 Subject: [PATCH 055/102] Adds PipeableAndBindOptionalSecond --- internal/iterbase.hpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/internal/iterbase.hpp b/internal/iterbase.hpp index 69db7599..6f654828 100644 --- a/internal/iterbase.hpp +++ b/internal/iterbase.hpp @@ -95,8 +95,8 @@ namespace iter { // iterator_type is the type of C's iterator template - using const_iterator_type = decltype( - get_begin(std::declval&>())); + using const_iterator_type = decltype(get_begin( + std::declval&>())); // iterator_deref is the type obtained by dereferencing an iterator // to an object of type C @@ -391,6 +391,37 @@ namespace iter { } }; + // Pipeable callable which allows binding of the second argument + // f(a, b) is the same as a | f(b) + // f(a) with an iterable is the same as f(a, DefaultT{}) + template + struct PipeableAndBindOptionalSecond : Pipeable { + protected: + template + struct FnPartial : Pipeable> { + mutable T stored_arg; + constexpr FnPartial(T in_t) : stored_arg(in_t) {} + + template + auto operator()(Container&& container) const { + return F{}(std::forward(container), stored_arg); + } + }; + + public: + template >> + FnPartial> operator()(T&& t) const { + return {std::forward(t)}; + } + + template >> + auto operator()(Container&& container) const { + return static_cast(*this)( + std::forward(container), DefaultT{}); + } + }; + // This is a complicated class to generate a callable that can work: // (1) with just a single (iterable) passed, and DefaultT substituted // (2) with an iterable and a callable From 06cc4fe4479b8e51a1bd08115ca370cbee5771ff Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 11:28:03 -0800 Subject: [PATCH 056/102] Updates unique_justseen overload to be pipeable Issue #90 --- test/test_unique_justseen.cpp | 11 +++++++++-- unique_justseen.hpp | 8 ++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test/test_unique_justseen.cpp b/test/test_unique_justseen.cpp index 455ac927..72af19b8 100644 --- a/test/test_unique_justseen.cpp +++ b/test/test_unique_justseen.cpp @@ -109,8 +109,15 @@ TEST_CASE("unique_justseen: works with key function", Vec vc{2, 3, 4, 2, 10, 2, 12, 10}; std::vector v; - for (auto&& iw : unique_justseen(iwv, IntWrapperKey{})) { - v.push_back(iw.n); + SECTION("Normal call") { + for (auto&& iw : unique_justseen(iwv, IntWrapperKey{})) { + v.push_back(iw.n); + } + } + SECTION("Pipe") { + for (auto&& iw : iwv | unique_justseen(IntWrapperKey{})) { + v.push_back(iw.n); + } } REQUIRE(v == vc); diff --git a/unique_justseen.hpp b/unique_justseen.hpp index b717392f..5a566e78 100644 --- a/unique_justseen.hpp +++ b/unique_justseen.hpp @@ -9,8 +9,9 @@ namespace iter { namespace impl { - struct UniqueJustseenFn : Pipeable { + struct UniqueJustseenFn : PipeableAndBindOptionalSecond { public: + using PipeableAndBindOptionalSecond::operator(); template auto operator()(Container&& container, KeyFunc key_fn) const { // decltype(auto) return type in lambda so reference types are preserved @@ -20,11 +21,6 @@ namespace iter { }, groupby(std::forward(container), key_fn)); } - - template - auto operator()(Container&& container) const { - return (*this)(std::forward(container), Identity{}); - } }; } constexpr impl::UniqueJustseenFn unique_justseen{}; From 8a8b6d0f12e9b5a1fe505820ba81d787b8c610c5 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 11:32:33 -0800 Subject: [PATCH 057/102] Adds unique_justseen with key to README Issue #90 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index c405d760..c9d99d3a 100644 --- a/README.md +++ b/README.md @@ -358,6 +358,15 @@ for (auto&& i : unique_justseen(v)) { } ``` +If elements cannot be directly compared with equality, you can pass in a key +callable. +```c++ +vector v { /* ... */ }; +for (auto&& p : unique_justseen(v, [] (const Person& p) { return p.name; })) + cout << p.name() << ' ' << p.age() << '\n'; +} +``` + takewhile --------- Yields elements from an iterable until the first element that is false under From add5acc932dea2c78acd80747bab71ec0b5bce27 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 30 Dec 2022 11:39:31 -0800 Subject: [PATCH 058/102] Switches flag from c++1z to c++17 --- examples/SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/SConstruct b/examples/SConstruct index 5f9110c9..cb5c373c 100644 --- a/examples/SConstruct +++ b/examples/SConstruct @@ -3,7 +3,7 @@ import os env = Environment( ENV=os.environ, CXXFLAGS= ['-g', '-Wall', '-Wextra', - '-pedantic', '-std=c++1z', + '-pedantic', '-std=c++17', '-I/usr/local/include' ], CPPPATH='..', From eecaaf71ee2103f2afef0567bc22ce1502cc1f0a Mon Sep 17 00:00:00 2001 From: Pedro Kaj Kjellerup Nacht Date: Tue, 6 Jun 2023 14:20:58 +0000 Subject: [PATCH 059/102] Add security policy Signed-off-by: Pedro Kaj Kjellerup Nacht --- SECURITY.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..201281d4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives me time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to haining.cpp@gmail.com; and/or +- send me a [private vulnerability report](https://github.com/ryanhaining/cppitertools/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +This project is maintained by a single maintainer on a reasonable-effort basis. As such, +I ask that you give me 90 days to work on a fix before public exposure. From 492c15aab96f4ca3938a6b734d6a08cb7feea75a Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 3 Jul 2023 20:37:50 -0700 Subject: [PATCH 060/102] Update travis-ci link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9d99d3a..64c1430f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Follow [@cppitertools](https://twitter.com/cppitertools) for updates. #### Build and Test Status Status | Compilers ---- | ---- -[![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://travis-ci.com/ryanhaining/cppitertools) | gcc-7 gcc-8 gcc-9 clang-5.0 clang-6.0 clang-7 clang-8 clang-9 +[![Travis Build Status](https://travis-ci.com/ryanhaining/cppitertools.svg?branch=master)](https://app.travis-ci.com/github/ryanhaining/cppitertools) | gcc-7 gcc-8 gcc-9 clang-5.0 clang-6.0 clang-7 clang-8 clang-9 [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/ryanhaining/cppitertools?svg=true)](https://ci.appveyor.com/project/ryanhaining/cppitertools) | MSVC 2017 MSVC 2019 #### Table of Contents From 556fca33235b6ad1fba6e37df104ca23a01a5b45 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 20 Sep 2023 23:38:56 -0700 Subject: [PATCH 061/102] Removes "recently changed" cmake warning fixes #97 --- CMakeLists.txt | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4f7eaeb..32d26fde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,6 @@ cmake_minimum_required(VERSION 3.12) project(cppitertools VERSION 2.0) -if(NOT DEFINED CACHE{cppitertools_INSTALL_CMAKE_DIR}) - message(WARNING [[ -The default value of cppitertools_INSTALL_CMAKE_DIR changed recently, from - "share/cppitertools/cmake" -to - "share" -in order to behave better with existing CMake practice. - -In order to get the previous behavior, pass - -Dcppitertools_INSTALL_CMAKE_DIR=share/cppitertools/cmake -to the CMake invocation; in order to get the new behavior without the warning, -pass - -Dcppitertools_INSTALL_CMAKE_DIR=share -explicitly. -]]) -endif() - # installation directories set(cppitertools_INSTALL_INCLUDE_DIR "include" CACHE STRING "The installation include directory") set(cppitertools_INSTALL_CMAKE_DIR "share" CACHE STRING "The installation cmake directory") From 9b2cee49f4ce9725513e9a12d303de9471078e89 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 16 Jan 2024 23:53:55 -0800 Subject: [PATCH 062/102] Modifies combinations(c, 0) to produce {{}} Instead of no elements, a sequence of a single empty iterable, to match python's behavior. Issue #101 --- combinations.hpp | 22 +++++++++++++++++++--- test/test_combinations.cpp | 28 ++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/combinations.hpp b/combinations.hpp index 7572142c..7aca3115 100644 --- a/combinations.hpp +++ b/combinations.hpp @@ -73,6 +73,12 @@ class iter::impl::Combinator { } } + static Iterator zero_length_end(ContainerT& container) { + Iterator it{container, 0}; + it.steps_ = 0; + return it; + } + CombIteratorDeref& operator*() { return indices_; } @@ -82,6 +88,11 @@ class iter::impl::Combinator { } Iterator& operator++() { + if (indices_.get().empty()) { + // zero-length case. + ++steps_; + return *this; + } for (auto iter = indices_.get().rbegin(); iter != indices_.get().rend(); ++iter) { ++(*iter); @@ -94,11 +105,10 @@ class iter::impl::Combinator { if (!(dumb_next(*iter, dist) != get_end(*container_p_))) { if ((iter + 1) != indices_.get().rend()) { size_t inc = 1; - for (auto down = iter; ; --down) { + for (auto down = iter;; --down) { (*down) = dumb_next(*(iter + 1), 1 + inc); ++inc; - if (down == indices_.get().rbegin()) - break; + if (down == indices_.get().rbegin()) break; } } else { steps_ = COMPLETE; @@ -138,6 +148,9 @@ class iter::impl::Combinator { } Iterator end() { + if (length_ == 0) { + return Iterator::zero_length_end(container_); + } return {container_, 0}; } @@ -146,6 +159,9 @@ class iter::impl::Combinator { } Iterator> end() const { + if (length_ == 0) { + return Iterator>::zero_length_end(container_); + } return {std::as_const(container_), 0}; } }; diff --git a/test/test_combinations.cpp b/test/test_combinations.cpp index f5c153cf..6d9718dc 100644 --- a/test/test_combinations.cpp +++ b/test/test_combinations.cpp @@ -5,7 +5,6 @@ #undef DEFINE_DEFAULT_ITERATOR_CTOR #include - #include #include #include @@ -95,10 +94,31 @@ TEST_CASE("combinations: size too large gives no results", "[combinations]") { REQUIRE(std::begin(c) == std::end(c)); } -TEST_CASE("combinations: size 0 gives nothing", "[combinations]") { +TEST_CASE("combinations: size 0 gives one empty result", "[combinations]") { std::string s{"ABCD"}; - auto c = combinations(s, 0); - REQUIRE(std::begin(c) == std::end(c)); + + CharCombSet ans = {{}}; + + CharCombSet sc; + for (auto&& v : combinations(s, 0)) { + sc.emplace_back(std::begin(v), std::end(v)); + } + + REQUIRE(ans == sc); +} + +TEST_CASE("combinations: size 0 gives one empty result for empty input", + "[combinations]") { + std::string s{}; + + CharCombSet ans = {{}}; + + CharCombSet sc; + for (auto&& v : combinations(s, 0)) { + sc.emplace_back(std::begin(v), std::end(v)); + } + + REQUIRE(ans == sc); } TEST_CASE( From 60171c0c987f98751f9b06a5e44b3e3733f429bd Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 16 Jan 2024 23:56:25 -0800 Subject: [PATCH 063/102] Modifies combinations_with_replacement(c, 0) to produce {{}} Issue #101 --- combinations_with_replacement.hpp | 22 ++++++++++++++++++--- test/test_combinations_with_replacement.cpp | 20 ++++++++++++++++--- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/combinations_with_replacement.hpp b/combinations_with_replacement.hpp index b7e20d7d..d501c6e1 100644 --- a/combinations_with_replacement.hpp +++ b/combinations_with_replacement.hpp @@ -60,6 +60,12 @@ class iter::impl::CombinatorWithReplacement { ? 0 : COMPLETE} {} + static Iterator zero_length_end(ContainerT& container) { + Iterator it{container, 0}; + it.steps_ = 0; + return it; + } + CombIteratorDeref& operator*() { return indices_; } @@ -69,15 +75,19 @@ class iter::impl::CombinatorWithReplacement { } Iterator& operator++() { + if (indices_.get().empty()) { + // zero-length case. + ++steps_; + return *this; + } for (auto iter = indices_.get().rbegin(); iter != indices_.get().rend(); ++iter) { ++(*iter); if (!(*iter != get_end(*container_p_))) { if ((iter + 1) != indices_.get().rend()) { - for (auto down = iter; ; --down) { + for (auto down = iter;; --down) { (*down) = dumb_next(*(iter + 1)); - if (down == indices_.get().rbegin()) - break; + if (down == indices_.get().rbegin()) break; } } else { steps_ = COMPLETE; @@ -117,6 +127,9 @@ class iter::impl::CombinatorWithReplacement { } Iterator end() { + if (length_ == 0) { + return Iterator::zero_length_end(container_); + } return {container_, 0}; } @@ -125,6 +138,9 @@ class iter::impl::CombinatorWithReplacement { } Iterator> end() const { + if (length_ == 0) { + return Iterator>::zero_length_end(container_); + } return {std::as_const(container_), 0}; } }; diff --git a/test/test_combinations_with_replacement.cpp b/test/test_combinations_with_replacement.cpp index 61f30120..536780ac 100644 --- a/test/test_combinations_with_replacement.cpp +++ b/test/test_combinations_with_replacement.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -96,8 +95,23 @@ TEST_CASE("combinations_with_replacement: big size is no problem", TEST_CASE("combinations_with_replacement: 0 size is empty", "[combinations_with_replacement]") { std::string s{"A"}; - auto cwr = combinations_with_replacement(s, 0); - REQUIRE(std::begin(cwr) == std::end(cwr)); + CharCombSet sc; + for (auto v : combinations_with_replacement(s, 0)) { + sc.emplace_back(std::begin(v), std::end(v)); + } + CharCombSet ans = {{}}; + REQUIRE(ans == sc); +} + +TEST_CASE("combinations_with_replacement: 0 size is empty with empty container", + "[combinations_with_replacement]") { + std::string s{}; + CharCombSet sc; + for (auto v : combinations_with_replacement(s, 0)) { + sc.emplace_back(std::begin(v), std::end(v)); + } + CharCombSet ans = {{}}; + REQUIRE(ans == sc); } TEST_CASE("combinations_with_replacement: operator->", From d26742747b4ba4e1dff0b078382b00a36676dcba Mon Sep 17 00:00:00 2001 From: Sayan Date: Tue, 16 Apr 2024 20:56:20 -0400 Subject: [PATCH 064/102] compare const and non-const chained iterators; all tests pass --- chain.hpp | 55 +++++++++++++++++++++++++++++---------------- test/test_chain.cpp | 9 +++----- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/chain.hpp b/chain.hpp index 23d1e054..55338c9d 100644 --- a/chain.hpp +++ b/chain.hpp @@ -1,10 +1,6 @@ #ifndef ITER_CHAIN_HPP_ #define ITER_CHAIN_HPP_ -#include "internal/iter_tuples.hpp" -#include "internal/iterator_wrapper.hpp" -#include "internal/iterbase.hpp" - #include #include #include @@ -12,6 +8,10 @@ #include #include +#include "internal/iter_tuples.hpp" +#include "internal/iterator_wrapper.hpp" +#include "internal/iterbase.hpp" + namespace iter { namespace impl { template @@ -43,6 +43,26 @@ class iter::impl::Chained { private: friend ChainMaker; + template + class IteratorDataPair { + IteratorDataPair() = delete; + + public: + using IterTupTypeA = iterator_tuple_type; + using IterTupTypeB = iterator_tuple_type; + + template + static bool get_and_check_not_equal( + const IterTupTypeA& lhs, const IterTupTypeB& rhs) { + return std::get(lhs) != std::get(rhs); + } + + using NeqFunc = bool (*)(const IterTupTypeA&, const IterTupTypeB&); + + constexpr static std::array neq_comparers{ + {get_and_check_not_equal...}}; + }; + template class IteratorData { IteratorData() = delete; @@ -76,16 +96,9 @@ class iter::impl::Chained { ++std::get(iters); } - template - static bool get_and_check_not_equal( - const IterTupType& lhs, const IterTupType& rhs) { - return std::get(lhs) != std::get(rhs); - } - using DerefFunc = DerefType (*)(IterTupType&); using ArrowFunc = ArrowType (*)(IterTupType&); using IncFunc = void (*)(IterTupType&); - using NeqFunc = bool (*)(const IterTupType&, const IterTupType&); constexpr static std::array derefers{ {get_and_deref...}}; @@ -96,9 +109,6 @@ class iter::impl::Chained { constexpr static std::array incrementers{ {get_and_increment...}}; - constexpr static std::array neq_comparers{ - {get_and_check_not_equal...}}; - using TraitsValue = iterator_traits_deref>; }; @@ -119,12 +129,16 @@ class iter::impl::Chained { void check_for_end_and_adjust() { while (index_ < sizeof...(Is) - && !(IterData::neq_comparers[index_](iters_, ends_))) { + && !(IteratorDataPair::neq_comparers[index_]( + iters_, ends_))) { ++index_; } } public: + template + friend class Iterator; + using iterator_category = std::input_iterator_tag; using value_type = typename IteratorData::TraitsValue; using difference_type = std::ptrdiff_t; @@ -141,7 +155,7 @@ class iter::impl::Chained { return IterData::derefers[index_](iters_); } - decltype(auto) operator-> () { + decltype(auto) operator->() { return IterData::arrowers[index_](iters_); } @@ -158,13 +172,16 @@ class iter::impl::Chained { } // TODO make const and non-const iterators comparable - bool operator!=(const Iterator& other) const { + template + bool operator!=(const Iterator& other) const { return index_ != other.index_ || (index_ != sizeof...(Is) - && IterData::neq_comparers[index_](iters_, other.iters_)); + && IteratorDataPair::neq_comparers[index_]( + iters_, other.iters_)); } - bool operator==(const Iterator& other) const { + template + bool operator==(const Iterator& other) const { return !(*this != other); } }; diff --git a/test/test_chain.cpp b/test/test_chain.cpp index b4b77cf2..5ad9c361 100644 --- a/test/test_chain.cpp +++ b/test/test_chain.cpp @@ -1,6 +1,4 @@ #include -#include "helpers.hpp" - #include #include #include @@ -8,6 +6,7 @@ #include #include "catch.hpp" +#include "helpers.hpp" using iter::chain; using itertest::BasicIterable; @@ -37,14 +36,12 @@ TEST_CASE("chain: const iteration", "[chain][const]") { REQUIRE(v == vc); } -// TODO make this work -#if 0 -TEST_CASE("chain: const iterators can be compared to non-const itertors", "[chain][const]") { +TEST_CASE("chain: const iterators can be compared to non-const itertors", + "[chain][const]") { auto ch = chain(std::string{}, std::string{}); const auto& cch = ch; (void)(std::begin(ch) == std::end(cch)); } -#endif TEST_CASE("chain: with different container types", "[chain]") { std::string s1{"abc"}; From 7119a5d7b00228130a47b7554c515bd5b403774f Mon Sep 17 00:00:00 2001 From: maxmarsc Date: Fri, 26 Apr 2024 13:15:11 +0200 Subject: [PATCH 065/102] Moved all headers into a cppitertools directory Removed test build files from git tracking --- CMakeLists.txt | 6 +++--- accumulate.hpp => cppitertools/accumulate.hpp | 0 batched.hpp => cppitertools/batched.hpp | 0 chain.hpp => cppitertools/chain.hpp | 0 chunked.hpp => cppitertools/chunked.hpp | 0 combinations.hpp => cppitertools/combinations.hpp | 0 .../combinations_with_replacement.hpp | 0 compress.hpp => cppitertools/compress.hpp | 0 count.hpp => cppitertools/count.hpp | 0 cycle.hpp => cppitertools/cycle.hpp | 0 dropwhile.hpp => cppitertools/dropwhile.hpp | 0 enumerate.hpp => cppitertools/enumerate.hpp | 0 filter.hpp => cppitertools/filter.hpp | 0 filterfalse.hpp => cppitertools/filterfalse.hpp | 0 groupby.hpp => cppitertools/groupby.hpp | 0 imap.hpp => cppitertools/imap.hpp | 0 {internal => cppitertools/internal}/iter_tuples.hpp | 0 .../internal}/iterator_wrapper.hpp | 0 .../internal}/iteratoriterator.hpp | 0 {internal => cppitertools/internal}/iterbase.hpp | 0 itertools.hpp => cppitertools/itertools.hpp | 0 permutations.hpp => cppitertools/permutations.hpp | 0 powerset.hpp => cppitertools/powerset.hpp | 0 product.hpp => cppitertools/product.hpp | 0 range.hpp => cppitertools/range.hpp | 0 repeat.hpp => cppitertools/repeat.hpp | 0 reversed.hpp => cppitertools/reversed.hpp | 0 slice.hpp => cppitertools/slice.hpp | 0 sliding_window.hpp => cppitertools/sliding_window.hpp | 0 sorted.hpp => cppitertools/sorted.hpp | 0 starmap.hpp => cppitertools/starmap.hpp | 0 takewhile.hpp => cppitertools/takewhile.hpp | 0 .../unique_everseen.hpp | 0 .../unique_justseen.hpp | 0 zip.hpp => cppitertools/zip.hpp | 0 zip_longest.hpp => cppitertools/zip_longest.hpp | 0 examples/accumulate_examples.cpp | 4 ++-- examples/batched_examples.cpp | 2 +- examples/chain_examples.cpp | 2 +- examples/chunked_examples.cpp | 2 +- examples/combinatoric_examples.cpp | 10 +++++----- examples/compress_examples.cpp | 2 +- examples/count_examples.cpp | 2 +- examples/cycle_examples.cpp | 2 +- examples/dropwhile_examples.cpp | 2 +- examples/enumerate_examples.cpp | 2 +- examples/filter_examples.cpp | 2 +- examples/filterfalse_examples.cpp | 2 +- examples/groupby_examples.cpp | 2 +- examples/imap_examples.cpp | 2 +- examples/mixed_examples.cpp | 4 ++-- examples/range_examples.cpp | 2 +- examples/repeat_examples.cpp | 2 +- examples/reversed_examples.cpp | 2 +- examples/slice_examples.cpp | 4 ++-- examples/sliding_window_examples.cpp | 2 +- examples/sorted_examples.cpp | 2 +- examples/starmap_examples.cpp | 2 +- examples/takewhile_examples.cpp | 2 +- examples/unique_everseen_examples.cpp | 2 +- examples/unique_justseen_examples.cpp | 2 +- examples/zip_examples.cpp | 2 +- examples/zip_longest_examples.cpp | 2 +- test/helpers.hpp | 2 +- test/test_accumulate.cpp | 2 +- test/test_batched.cpp | 2 +- test/test_chain.cpp | 2 +- test/test_chunked.cpp | 2 +- test/test_combinations.cpp | 2 +- test/test_combinations_with_replacement.cpp | 2 +- test/test_compress.cpp | 2 +- test/test_count.cpp | 2 +- test/test_cycle.cpp | 2 +- test/test_dropwhile.cpp | 2 +- test/test_enumerate.cpp | 2 +- test/test_filter.cpp | 2 +- test/test_filterfalse.cpp | 2 +- test/test_groupby.cpp | 2 +- test/test_imap.cpp | 2 +- test/test_iterator_wrapper.cpp | 2 +- test/test_iteratoriterator.cpp | 2 +- test/test_iterbase.cpp | 4 ++-- test/test_mixed.cpp | 2 +- test/test_permutations.cpp | 2 +- test/test_powerset.cpp | 2 +- test/test_product.cpp | 2 +- test/test_range.cpp | 2 +- test/test_repeat.cpp | 2 +- test/test_reversed.cpp | 2 +- test/test_slice.cpp | 2 +- test/test_sliding_window.cpp | 2 +- test/test_sorted.cpp | 4 ++-- test/test_starmap.cpp | 2 +- test/test_takewhile.cpp | 2 +- test/test_unique_everseen.cpp | 2 +- test/test_unique_justseen.cpp | 2 +- test/test_zip.cpp | 2 +- test/test_zip_longest.cpp | 2 +- 98 files changed, 74 insertions(+), 74 deletions(-) rename accumulate.hpp => cppitertools/accumulate.hpp (100%) rename batched.hpp => cppitertools/batched.hpp (100%) rename chain.hpp => cppitertools/chain.hpp (100%) rename chunked.hpp => cppitertools/chunked.hpp (100%) rename combinations.hpp => cppitertools/combinations.hpp (100%) rename combinations_with_replacement.hpp => cppitertools/combinations_with_replacement.hpp (100%) rename compress.hpp => cppitertools/compress.hpp (100%) rename count.hpp => cppitertools/count.hpp (100%) rename cycle.hpp => cppitertools/cycle.hpp (100%) rename dropwhile.hpp => cppitertools/dropwhile.hpp (100%) rename enumerate.hpp => cppitertools/enumerate.hpp (100%) rename filter.hpp => cppitertools/filter.hpp (100%) rename filterfalse.hpp => cppitertools/filterfalse.hpp (100%) rename groupby.hpp => cppitertools/groupby.hpp (100%) rename imap.hpp => cppitertools/imap.hpp (100%) rename {internal => cppitertools/internal}/iter_tuples.hpp (100%) rename {internal => cppitertools/internal}/iterator_wrapper.hpp (100%) rename {internal => cppitertools/internal}/iteratoriterator.hpp (100%) rename {internal => cppitertools/internal}/iterbase.hpp (100%) rename itertools.hpp => cppitertools/itertools.hpp (100%) rename permutations.hpp => cppitertools/permutations.hpp (100%) rename powerset.hpp => cppitertools/powerset.hpp (100%) rename product.hpp => cppitertools/product.hpp (100%) rename range.hpp => cppitertools/range.hpp (100%) rename repeat.hpp => cppitertools/repeat.hpp (100%) rename reversed.hpp => cppitertools/reversed.hpp (100%) rename slice.hpp => cppitertools/slice.hpp (100%) rename sliding_window.hpp => cppitertools/sliding_window.hpp (100%) rename sorted.hpp => cppitertools/sorted.hpp (100%) rename starmap.hpp => cppitertools/starmap.hpp (100%) rename takewhile.hpp => cppitertools/takewhile.hpp (100%) rename unique_everseen.hpp => cppitertools/unique_everseen.hpp (100%) rename unique_justseen.hpp => cppitertools/unique_justseen.hpp (100%) rename zip.hpp => cppitertools/zip.hpp (100%) rename zip_longest.hpp => cppitertools/zip_longest.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32d26fde..c7eb98ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(cppitertools::cppitertools ALIAS cppitertools) target_include_directories(cppitertools INTERFACE $ - $) + $) # require C++17 target_compile_features(cppitertools INTERFACE cxx_std_17) @@ -29,8 +29,8 @@ install( EXPORT cppitertools-targets) install( - DIRECTORY . - DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}/cppitertools) + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cppitertools + DESTINATION ${cppitertools_INSTALL_INCLUDE_DIR}) install( EXPORT cppitertools-targets diff --git a/accumulate.hpp b/cppitertools/accumulate.hpp similarity index 100% rename from accumulate.hpp rename to cppitertools/accumulate.hpp diff --git a/batched.hpp b/cppitertools/batched.hpp similarity index 100% rename from batched.hpp rename to cppitertools/batched.hpp diff --git a/chain.hpp b/cppitertools/chain.hpp similarity index 100% rename from chain.hpp rename to cppitertools/chain.hpp diff --git a/chunked.hpp b/cppitertools/chunked.hpp similarity index 100% rename from chunked.hpp rename to cppitertools/chunked.hpp diff --git a/combinations.hpp b/cppitertools/combinations.hpp similarity index 100% rename from combinations.hpp rename to cppitertools/combinations.hpp diff --git a/combinations_with_replacement.hpp b/cppitertools/combinations_with_replacement.hpp similarity index 100% rename from combinations_with_replacement.hpp rename to cppitertools/combinations_with_replacement.hpp diff --git a/compress.hpp b/cppitertools/compress.hpp similarity index 100% rename from compress.hpp rename to cppitertools/compress.hpp diff --git a/count.hpp b/cppitertools/count.hpp similarity index 100% rename from count.hpp rename to cppitertools/count.hpp diff --git a/cycle.hpp b/cppitertools/cycle.hpp similarity index 100% rename from cycle.hpp rename to cppitertools/cycle.hpp diff --git a/dropwhile.hpp b/cppitertools/dropwhile.hpp similarity index 100% rename from dropwhile.hpp rename to cppitertools/dropwhile.hpp diff --git a/enumerate.hpp b/cppitertools/enumerate.hpp similarity index 100% rename from enumerate.hpp rename to cppitertools/enumerate.hpp diff --git a/filter.hpp b/cppitertools/filter.hpp similarity index 100% rename from filter.hpp rename to cppitertools/filter.hpp diff --git a/filterfalse.hpp b/cppitertools/filterfalse.hpp similarity index 100% rename from filterfalse.hpp rename to cppitertools/filterfalse.hpp diff --git a/groupby.hpp b/cppitertools/groupby.hpp similarity index 100% rename from groupby.hpp rename to cppitertools/groupby.hpp diff --git a/imap.hpp b/cppitertools/imap.hpp similarity index 100% rename from imap.hpp rename to cppitertools/imap.hpp diff --git a/internal/iter_tuples.hpp b/cppitertools/internal/iter_tuples.hpp similarity index 100% rename from internal/iter_tuples.hpp rename to cppitertools/internal/iter_tuples.hpp diff --git a/internal/iterator_wrapper.hpp b/cppitertools/internal/iterator_wrapper.hpp similarity index 100% rename from internal/iterator_wrapper.hpp rename to cppitertools/internal/iterator_wrapper.hpp diff --git a/internal/iteratoriterator.hpp b/cppitertools/internal/iteratoriterator.hpp similarity index 100% rename from internal/iteratoriterator.hpp rename to cppitertools/internal/iteratoriterator.hpp diff --git a/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp similarity index 100% rename from internal/iterbase.hpp rename to cppitertools/internal/iterbase.hpp diff --git a/itertools.hpp b/cppitertools/itertools.hpp similarity index 100% rename from itertools.hpp rename to cppitertools/itertools.hpp diff --git a/permutations.hpp b/cppitertools/permutations.hpp similarity index 100% rename from permutations.hpp rename to cppitertools/permutations.hpp diff --git a/powerset.hpp b/cppitertools/powerset.hpp similarity index 100% rename from powerset.hpp rename to cppitertools/powerset.hpp diff --git a/product.hpp b/cppitertools/product.hpp similarity index 100% rename from product.hpp rename to cppitertools/product.hpp diff --git a/range.hpp b/cppitertools/range.hpp similarity index 100% rename from range.hpp rename to cppitertools/range.hpp diff --git a/repeat.hpp b/cppitertools/repeat.hpp similarity index 100% rename from repeat.hpp rename to cppitertools/repeat.hpp diff --git a/reversed.hpp b/cppitertools/reversed.hpp similarity index 100% rename from reversed.hpp rename to cppitertools/reversed.hpp diff --git a/slice.hpp b/cppitertools/slice.hpp similarity index 100% rename from slice.hpp rename to cppitertools/slice.hpp diff --git a/sliding_window.hpp b/cppitertools/sliding_window.hpp similarity index 100% rename from sliding_window.hpp rename to cppitertools/sliding_window.hpp diff --git a/sorted.hpp b/cppitertools/sorted.hpp similarity index 100% rename from sorted.hpp rename to cppitertools/sorted.hpp diff --git a/starmap.hpp b/cppitertools/starmap.hpp similarity index 100% rename from starmap.hpp rename to cppitertools/starmap.hpp diff --git a/takewhile.hpp b/cppitertools/takewhile.hpp similarity index 100% rename from takewhile.hpp rename to cppitertools/takewhile.hpp diff --git a/unique_everseen.hpp b/cppitertools/unique_everseen.hpp similarity index 100% rename from unique_everseen.hpp rename to cppitertools/unique_everseen.hpp diff --git a/unique_justseen.hpp b/cppitertools/unique_justseen.hpp similarity index 100% rename from unique_justseen.hpp rename to cppitertools/unique_justseen.hpp diff --git a/zip.hpp b/cppitertools/zip.hpp similarity index 100% rename from zip.hpp rename to cppitertools/zip.hpp diff --git a/zip_longest.hpp b/cppitertools/zip_longest.hpp similarity index 100% rename from zip_longest.hpp rename to cppitertools/zip_longest.hpp diff --git a/examples/accumulate_examples.cpp b/examples/accumulate_examples.cpp index 9d9a2f67..4d8569d2 100644 --- a/examples/accumulate_examples.cpp +++ b/examples/accumulate_examples.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include diff --git a/examples/batched_examples.cpp b/examples/batched_examples.cpp index c63c05b3..1dfa6abf 100644 --- a/examples/batched_examples.cpp +++ b/examples/batched_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/chain_examples.cpp b/examples/chain_examples.cpp index 607fa040..cdcd7fed 100644 --- a/examples/chain_examples.cpp +++ b/examples/chain_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/chunked_examples.cpp b/examples/chunked_examples.cpp index 9c3046f5..c3325df1 100644 --- a/examples/chunked_examples.cpp +++ b/examples/chunked_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/combinatoric_examples.cpp b/examples/combinatoric_examples.cpp index 653d428c..0915f985 100644 --- a/examples/combinatoric_examples.cpp +++ b/examples/combinatoric_examples.cpp @@ -1,8 +1,8 @@ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/examples/compress_examples.cpp b/examples/compress_examples.cpp index b60197a9..dbabbbb9 100644 --- a/examples/compress_examples.cpp +++ b/examples/compress_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/count_examples.cpp b/examples/count_examples.cpp index 10ac8a72..70b876cc 100644 --- a/examples/count_examples.cpp +++ b/examples/count_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/examples/cycle_examples.cpp b/examples/cycle_examples.cpp index 7f973d66..0598431a 100644 --- a/examples/cycle_examples.cpp +++ b/examples/cycle_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/dropwhile_examples.cpp b/examples/dropwhile_examples.cpp index 8c628f1a..c8f993c0 100644 --- a/examples/dropwhile_examples.cpp +++ b/examples/dropwhile_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/enumerate_examples.cpp b/examples/enumerate_examples.cpp index 899c3b39..822abbbf 100644 --- a/examples/enumerate_examples.cpp +++ b/examples/enumerate_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/filter_examples.cpp b/examples/filter_examples.cpp index d28c443a..5ce16af2 100644 --- a/examples/filter_examples.cpp +++ b/examples/filter_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/filterfalse_examples.cpp b/examples/filterfalse_examples.cpp index dd937cee..0edc004a 100644 --- a/examples/filterfalse_examples.cpp +++ b/examples/filterfalse_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/groupby_examples.cpp b/examples/groupby_examples.cpp index 8d4a9918..5525f9ff 100644 --- a/examples/groupby_examples.cpp +++ b/examples/groupby_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/imap_examples.cpp b/examples/imap_examples.cpp index 812281b6..95fee802 100644 --- a/examples/imap_examples.cpp +++ b/examples/imap_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/mixed_examples.cpp b/examples/mixed_examples.cpp index 08ff774b..9f1ba056 100644 --- a/examples/mixed_examples.cpp +++ b/examples/mixed_examples.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include diff --git a/examples/range_examples.cpp b/examples/range_examples.cpp index 1dd03199..ff328ee7 100644 --- a/examples/range_examples.cpp +++ b/examples/range_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/examples/repeat_examples.cpp b/examples/repeat_examples.cpp index 24d37a8d..2dceaf98 100644 --- a/examples/repeat_examples.cpp +++ b/examples/repeat_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/examples/reversed_examples.cpp b/examples/reversed_examples.cpp index d4d804d9..1bb8dbed 100644 --- a/examples/reversed_examples.cpp +++ b/examples/reversed_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/slice_examples.cpp b/examples/slice_examples.cpp index 42a16e33..d9f2501a 100644 --- a/examples/slice_examples.cpp +++ b/examples/slice_examples.cpp @@ -1,7 +1,7 @@ #include -#include -#include +#include +#include #include #include diff --git a/examples/sliding_window_examples.cpp b/examples/sliding_window_examples.cpp index ff286202..7658182d 100644 --- a/examples/sliding_window_examples.cpp +++ b/examples/sliding_window_examples.cpp @@ -1,4 +1,4 @@ -#include "sliding_window.hpp" +#include "cppitertools/sliding_window.hpp" #include #include diff --git a/examples/sorted_examples.cpp b/examples/sorted_examples.cpp index 109d137d..4e6b5a22 100644 --- a/examples/sorted_examples.cpp +++ b/examples/sorted_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/starmap_examples.cpp b/examples/starmap_examples.cpp index 9797756c..b375ef3e 100644 --- a/examples/starmap_examples.cpp +++ b/examples/starmap_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/takewhile_examples.cpp b/examples/takewhile_examples.cpp index 3062a141..d86d6442 100644 --- a/examples/takewhile_examples.cpp +++ b/examples/takewhile_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/unique_everseen_examples.cpp b/examples/unique_everseen_examples.cpp index a670f0b1..52dc02fa 100644 --- a/examples/unique_everseen_examples.cpp +++ b/examples/unique_everseen_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/unique_justseen_examples.cpp b/examples/unique_justseen_examples.cpp index 878cd4f8..7c8cd9aa 100644 --- a/examples/unique_justseen_examples.cpp +++ b/examples/unique_justseen_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/zip_examples.cpp b/examples/zip_examples.cpp index d35dd0cf..51febf13 100644 --- a/examples/zip_examples.cpp +++ b/examples/zip_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/examples/zip_longest_examples.cpp b/examples/zip_longest_examples.cpp index 5f8dc9e7..d3c80173 100644 --- a/examples/zip_longest_examples.cpp +++ b/examples/zip_longest_examples.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/helpers.hpp b/test/helpers.hpp index 93112519..80167980 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -1,7 +1,7 @@ #ifndef TEST_HELPER_H_ #define TEST_HELPER_H_ -#include +#include #include #include #include diff --git a/test/test_accumulate.cpp b/test/test_accumulate.cpp index f91f32b6..c868f0c5 100644 --- a/test/test_accumulate.cpp +++ b/test/test_accumulate.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" #include diff --git a/test/test_batched.cpp b/test/test_batched.cpp index bdd5e2a3..098acecc 100644 --- a/test/test_batched.cpp +++ b/test/test_batched.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_chain.cpp b/test/test_chain.cpp index b4b77cf2..9196ef72 100644 --- a/test/test_chain.cpp +++ b/test/test_chain.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" #include diff --git a/test/test_chunked.cpp b/test/test_chunked.cpp index 7e3165a0..3aaca797 100644 --- a/test/test_chunked.cpp +++ b/test/test_chunked.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_combinations.cpp b/test/test_combinations.cpp index 6d9718dc..14548240 100644 --- a/test/test_combinations.cpp +++ b/test/test_combinations.cpp @@ -4,7 +4,7 @@ #undef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE #undef DEFINE_DEFAULT_ITERATOR_CTOR -#include +#include #include #include #include diff --git a/test/test_combinations_with_replacement.cpp b/test/test_combinations_with_replacement.cpp index 536780ac..2c3ee0f6 100644 --- a/test/test_combinations_with_replacement.cpp +++ b/test/test_combinations_with_replacement.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/test/test_compress.cpp b/test/test_compress.cpp index 1330e9c2..70aa3f89 100644 --- a/test/test_compress.cpp +++ b/test/test_compress.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" #include diff --git a/test/test_count.cpp b/test/test_count.cpp index 8998f1eb..d5248ac2 100644 --- a/test/test_count.cpp +++ b/test/test_count.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" #include diff --git a/test/test_cycle.cpp b/test/test_cycle.cpp index 84981878..0974a87c 100644 --- a/test/test_cycle.cpp +++ b/test/test_cycle.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_dropwhile.cpp b/test/test_dropwhile.cpp index 2b85c607..2a41a671 100644 --- a/test/test_dropwhile.cpp +++ b/test/test_dropwhile.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_enumerate.cpp b/test/test_enumerate.cpp index d5fb4238..777146e6 100644 --- a/test/test_enumerate.cpp +++ b/test/test_enumerate.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_filter.cpp b/test/test_filter.cpp index b10dd5be..f160785b 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_filterfalse.cpp b/test/test_filterfalse.cpp index 7a74a0df..93d50d6a 100644 --- a/test/test_filterfalse.cpp +++ b/test/test_filterfalse.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_groupby.cpp b/test/test_groupby.cpp index 0069da14..6ce9d479 100644 --- a/test/test_groupby.cpp +++ b/test/test_groupby.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_imap.cpp b/test/test_imap.cpp index b2be404d..40a9d3e3 100644 --- a/test/test_imap.cpp +++ b/test/test_imap.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_iterator_wrapper.cpp b/test/test_iterator_wrapper.cpp index 6c79a278..e5b0238a 100644 --- a/test/test_iterator_wrapper.cpp +++ b/test/test_iterator_wrapper.cpp @@ -1,7 +1,7 @@ // NOTE this header tests implementation details #include "catch.hpp" -#include "internal/iterator_wrapper.hpp" +#include "cppitertools/internal/iterator_wrapper.hpp" // I'm using a std::vector of 1 int instead of just an int in order to give // the iterator types non-trivial constructors, destructors, and assignment. diff --git a/test/test_iteratoriterator.cpp b/test/test_iteratoriterator.cpp index dd2c4987..11c1b16c 100644 --- a/test/test_iteratoriterator.cpp +++ b/test/test_iteratoriterator.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_iterbase.cpp b/test/test_iterbase.cpp index c8783f32..c904b841 100644 --- a/test/test_iterbase.cpp +++ b/test/test_iterbase.cpp @@ -2,8 +2,8 @@ // on any of this. Users of the library must consider all of this undocumented // -#include -#include +#include +#include #include #include #include diff --git a/test/test_mixed.cpp b/test/test_mixed.cpp index 9c4c3ed6..b190adf6 100644 --- a/test/test_mixed.cpp +++ b/test/test_mixed.cpp @@ -4,7 +4,7 @@ #include #include "catch.hpp" -#include "itertools.hpp" +#include "cppitertools/itertools.hpp" class MyUnMovable { int val; diff --git a/test/test_permutations.cpp b/test/test_permutations.cpp index 5da85fc8..8d77d2f2 100644 --- a/test/test_permutations.cpp +++ b/test/test_permutations.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_powerset.cpp b/test/test_powerset.cpp index c18dd1f7..44a7d8b5 100644 --- a/test/test_powerset.cpp +++ b/test/test_powerset.cpp @@ -1,4 +1,4 @@ -#include +#include #define CHAR_RANGE_DEFAULT_CONSTRUCTIBLE #include "helpers.hpp" diff --git a/test/test_product.cpp b/test/test_product.cpp index 6a580bc9..a453da99 100644 --- a/test/test_product.cpp +++ b/test/test_product.cpp @@ -1,4 +1,4 @@ -#include +#include #define DEFINE_BASIC_ITERABLE_COPY_CTOR #define DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END diff --git a/test/test_range.cpp b/test/test_range.cpp index 4a25211a..b2b15f98 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -1,4 +1,4 @@ -#include "range.hpp" +#include "cppitertools/range.hpp" #include #include diff --git a/test/test_repeat.cpp b/test/test_repeat.cpp index ab92cbc1..5427909a 100644 --- a/test/test_repeat.cpp +++ b/test/test_repeat.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/test/test_reversed.cpp b/test/test_reversed.cpp index 530a17fc..10dfe4d9 100644 --- a/test/test_reversed.cpp +++ b/test/test_reversed.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_slice.cpp b/test/test_slice.cpp index 5fae78f4..fc5b42fe 100644 --- a/test/test_slice.cpp +++ b/test/test_slice.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_sliding_window.cpp b/test/test_sliding_window.cpp index a302e1dc..e031ce75 100644 --- a/test/test_sliding_window.cpp +++ b/test/test_sliding_window.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_sorted.cpp b/test/test_sorted.cpp index ff151576..ea80decf 100644 --- a/test/test_sorted.cpp +++ b/test/test_sorted.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -46,7 +46,7 @@ TEST_CASE("sorted: const iteration", "[sorted][const]") { REQUIRE(v == vc); } -//FIXME: This test currently fails (STL assertion fails on MSVC with debug library, simple test failure on gcc). The problem is 'sorted' will sort twice, once for non-const and once for const container; the resulting iterators are thus not on the same container (violating domain of == as specified in C++17 [forward.iterators]¶2). Remove [!hide] tag when fixed. +//FIXME: This test currently fails (STL assertion fails on MSVC with debug library, simple test failure on gcc). The problem is 'sorted' will sort twice, once for non-const and once for const container; the resulting iterators are thus not on the same container (violating domain of == as specified in C++17 [forward.iterators]�2). Remove [!hide] tag when fixed. TEST_CASE("sorted: const iterators can be compared to non-const iterators", "[sorted][const][!hide]") { auto s = sorted(Vec{1}); diff --git a/test/test_starmap.cpp b/test/test_starmap.cpp index feae8da9..e3958c84 100644 --- a/test/test_starmap.cpp +++ b/test/test_starmap.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" #include diff --git a/test/test_takewhile.cpp b/test/test_takewhile.cpp index 903f6a9f..e3dade73 100644 --- a/test/test_takewhile.cpp +++ b/test/test_takewhile.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/test/test_unique_everseen.cpp b/test/test_unique_everseen.cpp index 68cb319a..c5fedad3 100644 --- a/test/test_unique_everseen.cpp +++ b/test/test_unique_everseen.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_unique_justseen.cpp b/test/test_unique_justseen.cpp index 72af19b8..6147b477 100644 --- a/test/test_unique_justseen.cpp +++ b/test/test_unique_justseen.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_zip.cpp b/test/test_zip.cpp index cb49981c..87c927f8 100644 --- a/test/test_zip.cpp +++ b/test/test_zip.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" diff --git a/test/test_zip_longest.cpp b/test/test_zip_longest.cpp index 4759a412..cdf29269 100644 --- a/test/test_zip_longest.cpp +++ b/test/test_zip_longest.cpp @@ -1,4 +1,4 @@ -#include +#include #include "helpers.hpp" From 02f1457e23e277db646c6fe4cc519f5b1e9cc314 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 3 May 2024 17:02:49 -0700 Subject: [PATCH 066/102] Fixes BUILD for cppitertools/ prefix users will need to prefix includes with "cppitertools/" Issue #100 --- BUILD | 70 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/BUILD b/BUILD index bab14566..f2fab7df 100644 --- a/BUILD +++ b/BUILD @@ -1,43 +1,43 @@ cc_library( name = "cppitertools", hdrs = [ - "accumulate.hpp", - "batched.hpp", - "chain.hpp", - "chunked.hpp", - "combinations.hpp", - "combinations_with_replacement.hpp", - "compress.hpp", - "count.hpp", - "cycle.hpp", - "dropwhile.hpp", - "enumerate.hpp", - "filter.hpp", - "filterfalse.hpp", - "groupby.hpp", - "imap.hpp", - "itertools.hpp", - "permutations.hpp", - "powerset.hpp", - "product.hpp", - "range.hpp", - "repeat.hpp", - "reversed.hpp", - "slice.hpp", - "sliding_window.hpp", - "sorted.hpp", - "starmap.hpp", - "takewhile.hpp", - "unique_everseen.hpp", - "unique_justseen.hpp", - "zip.hpp", - "zip_longest.hpp", + "cppitertools/accumulate.hpp", + "cppitertools/batched.hpp", + "cppitertools/chain.hpp", + "cppitertools/chunked.hpp", + "cppitertools/combinations.hpp", + "cppitertools/combinations_with_replacement.hpp", + "cppitertools/compress.hpp", + "cppitertools/count.hpp", + "cppitertools/cycle.hpp", + "cppitertools/dropwhile.hpp", + "cppitertools/enumerate.hpp", + "cppitertools/filter.hpp", + "cppitertools/filterfalse.hpp", + "cppitertools/groupby.hpp", + "cppitertools/imap.hpp", + "cppitertools/itertools.hpp", + "cppitertools/permutations.hpp", + "cppitertools/powerset.hpp", + "cppitertools/product.hpp", + "cppitertools/range.hpp", + "cppitertools/repeat.hpp", + "cppitertools/reversed.hpp", + "cppitertools/slice.hpp", + "cppitertools/sliding_window.hpp", + "cppitertools/sorted.hpp", + "cppitertools/starmap.hpp", + "cppitertools/takewhile.hpp", + "cppitertools/unique_everseen.hpp", + "cppitertools/unique_justseen.hpp", + "cppitertools/zip.hpp", + "cppitertools/zip_longest.hpp", ], srcs = [ - "internal/iter_tuples.hpp", - "internal/iterator_wrapper.hpp", - "internal/iteratoriterator.hpp", - "internal/iterbase.hpp", + "cppitertools/internal/iter_tuples.hpp", + "cppitertools/internal/iterator_wrapper.hpp", + "cppitertools/internal/iteratoriterator.hpp", + "cppitertools/internal/iterbase.hpp", ], visibility = ["//visibility:public"], ) From f857d4ad101b5635842e6216e4d962c71da41cec Mon Sep 17 00:00:00 2001 From: Sayan Date: Sun, 5 May 2024 09:28:10 -0400 Subject: [PATCH 067/102] remove stale comments; improve tests for const non-const comparisons [chain] --- chain.hpp | 1 - test/test_chain.cpp | 54 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/chain.hpp b/chain.hpp index 55338c9d..85dd6555 100644 --- a/chain.hpp +++ b/chain.hpp @@ -171,7 +171,6 @@ class iter::impl::Chained { return ret; } - // TODO make const and non-const iterators comparable template bool operator!=(const Iterator& other) const { return index_ != other.index_ diff --git a/test/test_chain.cpp b/test/test_chain.cpp index 5ad9c361..47360d97 100644 --- a/test/test_chain.cpp +++ b/test/test_chain.cpp @@ -38,9 +38,31 @@ TEST_CASE("chain: const iteration", "[chain][const]") { TEST_CASE("chain: const iterators can be compared to non-const itertors", "[chain][const]") { - auto ch = chain(std::string{}, std::string{}); - const auto& cch = ch; - (void)(std::begin(ch) == std::end(cch)); + std::string s1{"abc"}; + std::list li{'m', 'n', 'o'}; + auto ch = chain(s1, li); + + const auto cch = chain(s1, li); + SECTION("begin and const begin compare equal") { + REQUIRE(std::begin(ch) == std::begin(cch)); + } + SECTION("begin and const end compare not-equal") { + REQUIRE_FALSE(std::begin(ch) == std::end(cch)); + } + SECTION("end and const end compare equal") { + REQUIRE(std::end(ch) == std::end(cch)); + } + SECTION( + "const and non-const iterator compare equal/not-equal at appropriate " + "pos.") { + auto iter = ch.begin(); + iter++; + auto citer = cch.begin(); + citer++; + REQUIRE(iter == citer); + citer++; + REQUIRE_FALSE(iter == citer); + } } TEST_CASE("chain: with different container types", "[chain]") { @@ -215,10 +237,32 @@ TEST_CASE( "chain.from_iterable: const iterators can be compared to non-const " "iterators", "[chain.from_iterable][const]") { - std::vector> v{}; + std::vector> v{{1, 2}, {4, 6}}; auto ch = chain.from_iterable(v); const auto& cch = ch; - (void)(std::begin(ch) == std::end(cch)); + + SECTION("begin and const end compare not-equal") { + REQUIRE_FALSE(std::begin(ch) == std::end(cch)); + } + SECTION("begin and const begin compare equal") { + REQUIRE(std::begin(ch) == std::begin(cch)); + } + SECTION("end and const end compare not-equal") { + REQUIRE(std::end(ch) == std::end(cch)); + } + SECTION( + "const and non-const iterator compare equal/not-equal at appropriate " + "pos.") { + auto iter = ch.begin(); + iter++; + auto citer = cch.begin(); + citer++; + REQUIRE(iter == citer); + citer++; + REQUIRE_FALSE(iter == citer); + iter++; + REQUIRE(iter == citer); + } } TEST_CASE("chain.fromm_iterable: Works with different begin and end types", From 7abcb2d8da270544df3b41bf623d50f3357f7d28 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 6 May 2024 12:49:22 -0700 Subject: [PATCH 068/102] Updates conanfile for cppitertools subdirectory/ And conan2 I think? I am not really sure what I'm doing here. Issue #100 --- conanfile.py | 61 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/conanfile.py b/conanfile.py index b47cbab4..114b620f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,35 +1,44 @@ -from conans import ConanFile, CMake - -import os +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps class CppIterTools(ConanFile): - name = "cppitertools" - version = "2.0" - author = "Ryan Haining " - homepage = "https://github.com/ryanhaining/cppitertools" + name = 'cppitertools' + version = '3.0' + author = 'Ryan Haining ' + homepage = 'https://github.com/ryanhaining/cppitertools' url = homepage - topics = ("conan", "itertools", "cppitertools") - license = 'BSD 2-Clause "Simplified" License' - description = "Range-based for loop add-ons inspired by the Python builtins and itertools library. " \ - "Like itertools and the Python3 builtins, this library uses lazy evaluation wherever possible." - settings = "build_type", "compiler", "os", "arch" - generators = "cmake", "cmake_find_package", "cmake_paths" - exports = "LICENSE.md" - - exports_sources = list() - for file in os.listdir("."): - if file.endswith(".hpp"): - exports_sources.append(str(file)) - print("found files: " + str(exports_sources)) - exports_sources = tuple(exports_sources) + \ - ("internal/*", "CMakeLists.txt", "cmake/dummy-config.cmake.in") - no_copy_source = True + topics = ('itertools', 'cppitertools') + license = "BSD 2-Clause 'Simplified' License" + description = 'Range-based for loop add-ons inspired by the Python builtins and itertools library. ' \ + 'Like itertools and the Python3 builtins, this library uses lazy evaluation wherever possible.' + settings = 'build_type', 'compiler', 'os', 'arch' + exports = 'LICENSE.md' - def package(self): + exports_sources = ( + 'cppitertools/*', + 'cppitertools/internal/*', + 'CMakeLists.txt', + 'cmake/dummy-config.cmake.in') + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self) + tc.generate() + + def build(self): cmake = CMake(self) cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) cmake.install() - def package_id(self): - self.info.header_only() + def package_info(self): + self.cpp_info.bindirs = [] + self.cpp_info.libdirs = [] From 87ad8a0b3de4a10cf475c01e0ccdf517529b4142 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 21 Jun 2024 11:46:33 -0700 Subject: [PATCH 069/102] Adds bazel lockfiles to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index ac51a054..553aa4a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bazel-* +MODULE.bazel +MODULE.bazel.lock From 69d3b3f510847bdb953894f81cf6f52b9b533b8f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 21 Jun 2024 11:53:32 -0700 Subject: [PATCH 070/102] Removes anonymous namespace around chain definition Fixes #104 --- cppitertools/chain.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cppitertools/chain.hpp b/cppitertools/chain.hpp index 85dd6555..76759eeb 100644 --- a/cppitertools/chain.hpp +++ b/cppitertools/chain.hpp @@ -343,9 +343,7 @@ class iter::impl::ChainMaker { }; namespace iter { - namespace { - constexpr auto chain = iter::impl::ChainMaker{}; - } + inline constexpr auto chain = iter::impl::ChainMaker{}; } #endif From ab4999407f73ca09cc2e5efff2b8724ac40b071a Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 21 Jun 2024 11:54:29 -0700 Subject: [PATCH 071/102] Adds inline specifier to itertools callable objects in headers Related to issue #104 --- cppitertools/accumulate.hpp | 2 +- cppitertools/batched.hpp | 2 +- cppitertools/chunked.hpp | 2 +- cppitertools/combinations.hpp | 2 +- cppitertools/combinations_with_replacement.hpp | 2 +- cppitertools/cycle.hpp | 2 +- cppitertools/dropwhile.hpp | 2 +- cppitertools/enumerate.hpp | 2 +- cppitertools/filter.hpp | 2 +- cppitertools/filterfalse.hpp | 2 +- cppitertools/groupby.hpp | 2 +- cppitertools/imap.hpp | 2 +- cppitertools/permutations.hpp | 2 +- cppitertools/powerset.hpp | 2 +- cppitertools/reversed.hpp | 2 +- cppitertools/slice.hpp | 2 +- cppitertools/sliding_window.hpp | 2 +- cppitertools/sorted.hpp | 2 +- cppitertools/starmap.hpp | 2 +- cppitertools/takewhile.hpp | 2 +- cppitertools/unique_everseen.hpp | 2 +- cppitertools/unique_justseen.hpp | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cppitertools/accumulate.hpp b/cppitertools/accumulate.hpp index 94c0c369..2c5bc54e 100644 --- a/cppitertools/accumulate.hpp +++ b/cppitertools/accumulate.hpp @@ -17,7 +17,7 @@ namespace iter { using AccumulateFn = IterToolFnOptionalBindSecond>; } - constexpr impl::AccumulateFn accumulate{}; + inline constexpr impl::AccumulateFn accumulate{}; } template diff --git a/cppitertools/batched.hpp b/cppitertools/batched.hpp index e5eadf13..f3d00ece 100644 --- a/cppitertools/batched.hpp +++ b/cppitertools/batched.hpp @@ -20,7 +20,7 @@ namespace iter { using BatchedFn = IterToolFnBindSizeTSecond; } - constexpr impl::BatchedFn batched{}; + inline constexpr impl::BatchedFn batched{}; } template diff --git a/cppitertools/chunked.hpp b/cppitertools/chunked.hpp index 1d03b6e1..13fa652f 100644 --- a/cppitertools/chunked.hpp +++ b/cppitertools/chunked.hpp @@ -20,7 +20,7 @@ namespace iter { using ChunkedFn = IterToolFnBindSizeTSecond; } - constexpr impl::ChunkedFn chunked{}; + inline constexpr impl::ChunkedFn chunked{}; } template diff --git a/cppitertools/combinations.hpp b/cppitertools/combinations.hpp index 7aca3115..cdee9cdd 100644 --- a/cppitertools/combinations.hpp +++ b/cppitertools/combinations.hpp @@ -15,7 +15,7 @@ namespace iter { using CombinationsFn = IterToolFnBindSizeTSecond; } - constexpr impl::CombinationsFn combinations{}; + inline constexpr impl::CombinationsFn combinations{}; } template diff --git a/cppitertools/combinations_with_replacement.hpp b/cppitertools/combinations_with_replacement.hpp index d501c6e1..39db37bd 100644 --- a/cppitertools/combinations_with_replacement.hpp +++ b/cppitertools/combinations_with_replacement.hpp @@ -15,7 +15,7 @@ namespace iter { using CombinationsWithReplacementFn = IterToolFnBindSizeTSecond; } - constexpr impl::CombinationsWithReplacementFn combinations_with_replacement{}; + inline constexpr impl::CombinationsWithReplacementFn combinations_with_replacement{}; } template diff --git a/cppitertools/cycle.hpp b/cppitertools/cycle.hpp index 1d3ecaef..fb3ca9c7 100644 --- a/cppitertools/cycle.hpp +++ b/cppitertools/cycle.hpp @@ -15,7 +15,7 @@ namespace iter { using CycleFn = IterToolFn; } - constexpr impl::CycleFn cycle{}; + inline constexpr impl::CycleFn cycle{}; } template diff --git a/cppitertools/dropwhile.hpp b/cppitertools/dropwhile.hpp index dc1874b0..e751bfe3 100644 --- a/cppitertools/dropwhile.hpp +++ b/cppitertools/dropwhile.hpp @@ -16,7 +16,7 @@ namespace iter { using DropWhileFn = IterToolFnOptionalBindFirst; } - constexpr impl::DropWhileFn dropwhile{}; + inline constexpr impl::DropWhileFn dropwhile{}; } template diff --git a/cppitertools/enumerate.hpp b/cppitertools/enumerate.hpp index e2dda12b..5d59feab 100644 --- a/cppitertools/enumerate.hpp +++ b/cppitertools/enumerate.hpp @@ -33,7 +33,7 @@ namespace iter { using EnumerateFn = IterToolFnOptionalBindSecond; } - constexpr impl::EnumerateFn enumerate{}; + inline constexpr impl::EnumerateFn enumerate{}; } namespace std { diff --git a/cppitertools/filter.hpp b/cppitertools/filter.hpp index 2a632be2..1e069070 100644 --- a/cppitertools/filter.hpp +++ b/cppitertools/filter.hpp @@ -24,7 +24,7 @@ namespace iter { using FilterFn = IterToolFnOptionalBindFirst; } - constexpr impl::FilterFn filter{}; + inline constexpr impl::FilterFn filter{}; } template diff --git a/cppitertools/filterfalse.hpp b/cppitertools/filterfalse.hpp index f0f22628..4056c402 100644 --- a/cppitertools/filterfalse.hpp +++ b/cppitertools/filterfalse.hpp @@ -38,7 +38,7 @@ namespace iter { using FilterFalseFn = IterToolFnOptionalBindFirst; } - constexpr impl::FilterFalseFn filterfalse{}; + inline constexpr impl::FilterFalseFn filterfalse{}; } // Delegates to Filtered with PredicateFlipper diff --git a/cppitertools/groupby.hpp b/cppitertools/groupby.hpp index cac82426..6d7453f9 100644 --- a/cppitertools/groupby.hpp +++ b/cppitertools/groupby.hpp @@ -20,7 +20,7 @@ namespace iter { using GroupByFn = IterToolFnOptionalBindSecond; } - constexpr impl::GroupByFn groupby{}; + inline constexpr impl::GroupByFn groupby{}; } template diff --git a/cppitertools/imap.hpp b/cppitertools/imap.hpp index 1e84faf0..80b8cc3d 100644 --- a/cppitertools/imap.hpp +++ b/cppitertools/imap.hpp @@ -21,7 +21,7 @@ namespace iter { using PipeableAndBindFirst::operator(); }; } - constexpr impl::IMapFn imap{}; + inline constexpr impl::IMapFn imap{}; } #endif diff --git a/cppitertools/permutations.hpp b/cppitertools/permutations.hpp index e10872d4..f567bc68 100644 --- a/cppitertools/permutations.hpp +++ b/cppitertools/permutations.hpp @@ -17,7 +17,7 @@ namespace iter { class Permuter; using PermutationsFn = IterToolFn; } - constexpr impl::PermutationsFn permutations{}; + inline constexpr impl::PermutationsFn permutations{}; } template diff --git a/cppitertools/powerset.hpp b/cppitertools/powerset.hpp index 3439069d..d6131ca8 100644 --- a/cppitertools/powerset.hpp +++ b/cppitertools/powerset.hpp @@ -18,7 +18,7 @@ namespace iter { using PowersetFn = IterToolFn; } - constexpr impl::PowersetFn powerset{}; + inline constexpr impl::PowersetFn powerset{}; } template diff --git a/cppitertools/reversed.hpp b/cppitertools/reversed.hpp index 3b914bb1..022088f6 100644 --- a/cppitertools/reversed.hpp +++ b/cppitertools/reversed.hpp @@ -45,7 +45,7 @@ namespace iter { using ReversedFn = IterToolFn; } - constexpr impl::ReversedFn reversed{}; + inline constexpr impl::ReversedFn reversed{}; } template diff --git a/cppitertools/slice.hpp b/cppitertools/slice.hpp index a926ecbb..38557016 100644 --- a/cppitertools/slice.hpp +++ b/cppitertools/slice.hpp @@ -172,7 +172,7 @@ struct iter::impl::SliceFn { }; namespace iter { - constexpr impl::SliceFn slice{}; + inline constexpr impl::SliceFn slice{}; } #endif diff --git a/cppitertools/sliding_window.hpp b/cppitertools/sliding_window.hpp index d4ab6cbc..01c348ba 100644 --- a/cppitertools/sliding_window.hpp +++ b/cppitertools/sliding_window.hpp @@ -16,7 +16,7 @@ namespace iter { class WindowSlider; using SlidingWindowFn = IterToolFnBindSizeTSecond; } - constexpr impl::SlidingWindowFn sliding_window{}; + inline constexpr impl::SlidingWindowFn sliding_window{}; } template diff --git a/cppitertools/sorted.hpp b/cppitertools/sorted.hpp index 4e963228..a704af3f 100644 --- a/cppitertools/sorted.hpp +++ b/cppitertools/sorted.hpp @@ -15,7 +15,7 @@ namespace iter { class SortedView; using SortedFn = IterToolFnOptionalBindSecond>; } - constexpr impl::SortedFn sorted{}; + inline constexpr impl::SortedFn sorted{}; } template diff --git a/cppitertools/starmap.hpp b/cppitertools/starmap.hpp index fbeaad68..f9c2d260 100644 --- a/cppitertools/starmap.hpp +++ b/cppitertools/starmap.hpp @@ -252,7 +252,7 @@ struct iter::impl::StarMapFn : PipeableAndBindFirst { }; namespace iter { - constexpr impl::StarMapFn starmap{}; + inline constexpr impl::StarMapFn starmap{}; } #endif diff --git a/cppitertools/takewhile.hpp b/cppitertools/takewhile.hpp index a04d86b2..338e43aa 100644 --- a/cppitertools/takewhile.hpp +++ b/cppitertools/takewhile.hpp @@ -16,7 +16,7 @@ namespace iter { using TakeWhileFn = IterToolFnOptionalBindFirst; } - constexpr impl::TakeWhileFn takewhile{}; + inline constexpr impl::TakeWhileFn takewhile{}; } template diff --git a/cppitertools/unique_everseen.hpp b/cppitertools/unique_everseen.hpp index 1e138d63..2195a4bd 100644 --- a/cppitertools/unique_everseen.hpp +++ b/cppitertools/unique_everseen.hpp @@ -45,7 +45,7 @@ namespace iter { }; } - constexpr impl::UniqueEverseenFn unique_everseen{}; + inline constexpr impl::UniqueEverseenFn unique_everseen{}; } #endif diff --git a/cppitertools/unique_justseen.hpp b/cppitertools/unique_justseen.hpp index 5a566e78..c4ca47f5 100644 --- a/cppitertools/unique_justseen.hpp +++ b/cppitertools/unique_justseen.hpp @@ -23,7 +23,7 @@ namespace iter { } }; } - constexpr impl::UniqueJustseenFn unique_justseen{}; + inline constexpr impl::UniqueJustseenFn unique_justseen{}; } #endif From 78a4eaa8a3b3b45aeea1022fd8a9402321729358 Mon Sep 17 00:00:00 2001 From: Udaya Prakash Date: Wed, 21 Aug 2024 16:33:29 +0000 Subject: [PATCH 072/102] Add bzlmod support to cppitertools --- .bazelrc | 2 ++ .bazelversion | 1 + .gitignore | 1 - MODULE.bazel | 3 +++ 4 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 MODULE.bazel diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 00000000..cbb8025a --- /dev/null +++ b/.bazelrc @@ -0,0 +1,2 @@ +# workspace support is deprecated in Bazel 7 +common --enable_bzlmod \ No newline at end of file diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000..34a8f745 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +7.3.1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 553aa4a3..0d4fed27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ bazel-* -MODULE.bazel MODULE.bazel.lock diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 00000000..d8dc8f72 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,3 @@ +module( + name = "cppitertools" +) \ No newline at end of file From 88de5d7cb3316231fce6e2a87e904cf3077a3def Mon Sep 17 00:00:00 2001 From: Udaya Prakash Date: Wed, 21 Aug 2024 16:36:26 +0000 Subject: [PATCH 073/102] add new line --- .bazelversion | 2 +- MODULE.bazel | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bazelversion b/.bazelversion index 34a8f745..643916c0 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.3.1 \ No newline at end of file +7.3.1 diff --git a/MODULE.bazel b/MODULE.bazel index d8dc8f72..b4a4568e 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,3 +1,3 @@ module( name = "cppitertools" -) \ No newline at end of file +) From e06f15357cc9ca78029e4158bee67da9f110e34f Mon Sep 17 00:00:00 2001 From: Udaya Prakash Date: Wed, 21 Aug 2024 16:37:36 +0000 Subject: [PATCH 074/102] remove bazelrc file --- .bazelrc | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .bazelrc diff --git a/.bazelrc b/.bazelrc deleted file mode 100644 index cbb8025a..00000000 --- a/.bazelrc +++ /dev/null @@ -1,2 +0,0 @@ -# workspace support is deprecated in Bazel 7 -common --enable_bzlmod \ No newline at end of file From 5a7f4aa357ed9b0ad59823e3d2acd57217d5beaf Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 18 Oct 2024 09:55:13 -0700 Subject: [PATCH 075/102] Escapes '<' and '>' in zip_longest description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64c1430f..7b43c218 100644 --- a/README.md +++ b/README.md @@ -579,7 +579,7 @@ Repeatedly yields a tuple of `boost::optional`s where `T` is the type yielded by the sequences' respective iterators. Because of its boost dependency, `zip_longest` is not in `itertools.hpp` and must be included separately. -The following loop prints either "Just " or "Nothing" for each +The following loop prints either "Just \" or "Nothing" for each element in each tuple yielded. ```c++ From 516115a959c92f5b7f133e4f52bcd7e701c46069 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 5 Feb 2025 16:48:51 -0800 Subject: [PATCH 076/102] Updates .bazelversion to 8.0.1 --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index 643916c0..cd1d2e94 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.3.1 +8.0.1 From 371888d0f9c1ee687c8c1241c8c86ebed26929d6 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Thu, 6 Feb 2025 16:41:33 -0800 Subject: [PATCH 077/102] Adds undefinide and address sanitizer to tests --- test/BUILD | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/BUILD b/test/BUILD index 9ceefecd..cc9aefe6 100644 --- a/test/BUILD +++ b/test/BUILD @@ -37,10 +37,13 @@ progs = [ "helpers", ] +SANITIZE = "-fsanitize=address,undefined" + cc_library( name = "test_main", srcs = ["test_main.cpp", "catch.hpp"], - copts = ["-std=c++17", "-g"] + copts = [SANITIZE, "-Wall", "-Wextra", "-std=c++17", "-g"], + linkopts = [SANITIZE], ) itertools_tests(progs) From d4c7340b44e141874b749a2c08489cb3ae180a71 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Thu, 6 Feb 2025 16:42:19 -0800 Subject: [PATCH 078/102] Suppresses bad dangling reference warning Fixes #108 --- cppitertools/internal/iterbase.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index 6f654828..9a8b33ba 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -353,6 +353,9 @@ namespace iter { template struct Pipeable { template +#if defined(__GNUC__) && !defined(__clang__) + [[gnu::no_dangling]] +#endif friend decltype(auto) operator|(T&& x, const Pipeable& p) { return static_cast(p)(std::forward(x)); } From fa6ee1a8b93c374185abd69e374ce9f4fd054cb4 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 10 Feb 2025 12:44:31 -0800 Subject: [PATCH 079/102] Moves utility callables into helpers.hpp Cleanup while doing #89 --- test/helpers.hpp | 16 +++++++++++++++ test/test_dropwhile.cpp | 24 +--------------------- test/test_filter.cpp | 22 +------------------- test/test_filterfalse.cpp | 22 +------------------- test/test_takewhile.cpp | 43 +++++++++++++-------------------------- 5 files changed, 33 insertions(+), 94 deletions(-) diff --git a/test/helpers.hpp b/test/helpers.hpp index 80167980..57f8a372 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -414,4 +414,20 @@ class IntCharPairRange : DiffEndRange, IncIntCharPair>({0, 'a'}, stop) {} }; +inline bool less_than_five(int i) { + return i < 5; +} + +class LessThanValue { + private: + int compare_val; + + public: + LessThanValue(int v) : compare_val(v) {} + + bool operator()(int i) { + return i < this->compare_val; + } +}; + #endif diff --git a/test/test_dropwhile.cpp b/test/test_dropwhile.cpp index 2a41a671..97c947f4 100644 --- a/test/test_dropwhile.cpp +++ b/test/test_dropwhile.cpp @@ -1,31 +1,15 @@ #include - -#include "helpers.hpp" - #include #include #include #include "catch.hpp" +#include "helpers.hpp" using iter::dropwhile; using Vec = const std::vector; -namespace { - class LessThanValue { - private: - int compare_val; - - public: - LessThanValue(int v) : compare_val(v) {} - - bool operator()(int i) { - return i < this->compare_val; - } - }; -} - TEST_CASE("dropwhile: skips initial elements", "[dropwhile]") { Vec ns{1, 2, 3, 4, 5, 6, 7, 8}; std::vector v; @@ -147,12 +131,6 @@ TEST_CASE("dropwhile: operator->", "[dropwhile]") { REQUIRE(it->size() == 6); } -namespace { - int less_than_five(int i) { - return i < 5; - } -} - TEST_CASE("dropwhile: works with function pointer", "[dropwhile]") { Vec ns{1, 2, 3, 4, 5, 6, 7, 8}; auto d = dropwhile(less_than_five, ns); diff --git a/test/test_filter.cpp b/test/test_filter.cpp index f160785b..a990844a 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -1,35 +1,15 @@ #include - -#include "helpers.hpp" - #include #include #include #include "catch.hpp" +#include "helpers.hpp" using iter::filter; using Vec = const std::vector; -namespace { - bool less_than_five(int i) { - return i < 5; - } - - class LessThanValue { - private: - int compare_val; - - public: - LessThanValue(int v) : compare_val(v) {} - - bool operator()(int i) { - return i < this->compare_val; - } - }; -} - TEST_CASE("filter: handles different callable types", "[filter]") { Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5}; Vec vc = {1, 2, 3, 1, -1}; diff --git a/test/test_filterfalse.cpp b/test/test_filterfalse.cpp index 93d50d6a..9a1dd4bc 100644 --- a/test/test_filterfalse.cpp +++ b/test/test_filterfalse.cpp @@ -1,35 +1,15 @@ #include - -#include "helpers.hpp" - #include #include #include #include "catch.hpp" +#include "helpers.hpp" using iter::filterfalse; using Vec = const std::vector; -namespace { - bool less_than_five(int i) { - return i < 5; - } - - class LessThanValue { - private: - int compare_val; - - public: - LessThanValue(int v) : compare_val(v) {} - - bool operator()(int i) { - return i < this->compare_val; - } - }; -} - TEST_CASE("filterfalse: handles different callable types", "[filterfalse]") { Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5}; Vec vc = {5, 6, 7, 5}; diff --git a/test/test_takewhile.cpp b/test/test_takewhile.cpp index e3dade73..0be43f84 100644 --- a/test/test_takewhile.cpp +++ b/test/test_takewhile.cpp @@ -1,6 +1,5 @@ -#include - #include +#include #include #include #include @@ -11,48 +10,34 @@ using iter::takewhile; using Vec = const std::vector; -namespace { - bool under_ten(int i) { - return i < 10; - } - - struct UnderTen { - bool operator()(int i) { - return i < 10; - } - }; -} - TEST_CASE("takewhile: works with lambda, callable, and function pointer", "[takewhile]") { - Vec ns = {1, 3, 5, 20, 2, 4, 6, 8}; + Vec ns = {1, 3, 4, 20, 2, 4, 6, 8}; + const Vec vc = {1, 3, 4}; SECTION("function pointer") { - auto tw = takewhile(under_ten, ns); + auto tw = takewhile(less_than_five, ns); Vec v(std::begin(tw), std::end(tw)); - Vec vc = {1, 3, 5}; REQUIRE(v == vc); } SECTION("callable object") { std::vector v; SECTION("Normal call") { - auto tw = takewhile(UnderTen{}, ns); + auto tw = takewhile(LessThanValue{10}, ns); v.assign(std::begin(tw), std::end(tw)); } SECTION("Pipe") { - auto tw = ns | takewhile(UnderTen{}); + auto tw = ns | takewhile(LessThanValue{10}); v.assign(std::begin(tw), std::end(tw)); } - Vec vc = {1, 3, 5}; REQUIRE(v == vc); } SECTION("lambda") { auto tw = takewhile([](int i) { return i < 10; }, ns); Vec v(std::begin(tw), std::end(tw)); - Vec vc = {1, 3, 5}; REQUIRE(v == vc); } } @@ -78,7 +63,7 @@ TEST_CASE("takewhile: handles pointer to member", "[takewhile]") { TEST_CASE("takewhile: supports const iteration", "[takewhile][const]") { Vec ns = {1, 3, 5, 20, 2, 4, 6, 8}; - const auto tw = takewhile(UnderTen{}, ns); + const auto tw = takewhile(LessThanValue{10}, ns); Vec v(std::begin(tw), std::end(tw)); Vec vc = {1, 3, 5}; REQUIRE(v == vc); @@ -86,7 +71,7 @@ TEST_CASE("takewhile: supports const iteration", "[takewhile][const]") { TEST_CASE("takewhile: const iterator and non-const iterator are comparable", "[takewhile][const]") { - auto tw = takewhile(UnderTen{}, Vec{}); + auto tw = takewhile(LessThanValue{10}, Vec{}); const auto& ctw = tw; (void)(std::begin(tw) == std::end(ctw)); } @@ -117,14 +102,14 @@ TEST_CASE("takewhile: identity", "[takewhile]") { TEST_CASE("takewhile: everything passes predicate", "[takewhile]") { Vec ns{1, 2, 3}; - auto tw = takewhile(under_ten, ns); + auto tw = takewhile(less_than_five, ns); Vec v(std::begin(tw), std::end(tw)); Vec vc = {1, 2, 3}; } TEST_CASE("takewhile: empty iterable is empty", "[takewhile]") { Vec ns{}; - auto tw = takewhile(under_ten, ns); + auto tw = takewhile(less_than_five, ns); SECTION("normal compare") { REQUIRE(std::begin(tw) == std::end(tw)); } @@ -138,7 +123,7 @@ TEST_CASE( "[takewhile]") { SECTION("First element is only element") { Vec ns = {20}; - auto tw = takewhile(under_ten, ns); + auto tw = takewhile(less_than_five, ns); SECTION("normal compare") { REQUIRE(std::begin(tw) == std::end(tw)); } @@ -149,7 +134,7 @@ TEST_CASE( SECTION("First element followed by elements that pass") { Vec ns = {20, 1, 1}; - auto tw = takewhile(under_ten, ns); + auto tw = takewhile(less_than_five, ns); SECTION("normal compare") { REQUIRE(std::begin(tw) == std::end(tw)); } @@ -161,10 +146,10 @@ TEST_CASE( TEST_CASE("takewhile: moves rvalues, binds to lvalues", "[takewhile]") { itertest::BasicIterable bi{1, 2}; - takewhile(under_ten, bi); + takewhile(less_than_five, bi); REQUIRE_FALSE(bi.was_moved_from()); - takewhile(under_ten, std::move(bi)); + takewhile(less_than_five, std::move(bi)); REQUIRE(bi.was_moved_from()); } From f9ce3d8d4eff145061df406f14265a4cc8946d6f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 10 Feb 2025 13:03:10 -0800 Subject: [PATCH 080/102] Adds MoveOnlyLessThanValue for testing For #89 --- test/helpers.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/helpers.hpp b/test/helpers.hpp index 57f8a372..94fd08f1 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -430,4 +430,22 @@ class LessThanValue { } }; +class MoveOnlyLessThanValue { + private: + int compare_val; + + public: + MoveOnlyLessThanValue(int v) : compare_val(v) {} + + MoveOnlyLessThanValue(const MoveOnlyLessThanValue&) = delete; + MoveOnlyLessThanValue& operator=(const MoveOnlyLessThanValue&) = delete; + + MoveOnlyLessThanValue(MoveOnlyLessThanValue&&) = default; + MoveOnlyLessThanValue& operator=(MoveOnlyLessThanValue&&) = default; + + bool operator()(int i) { + return i < this->compare_val; + } +}; + #endif From 420583c1df803566942e1c70c61ef5986d85006e Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 10 Feb 2025 13:10:55 -0800 Subject: [PATCH 081/102] IncludeBlocks: Preserve in clang-format --- .clang-format | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-format b/.clang-format index 650739d3..d9d9622a 100644 --- a/.clang-format +++ b/.clang-format @@ -8,5 +8,6 @@ BreakBeforeBinaryOperators: NonAssignment DerivePointerAlignment: false NamespaceIndentation: All FixNamespaceComments: false +IncludeBlocks: Preserve ... From 1c828d06d73d6bdd1ee6649619ef61b459d0957a Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Mon, 10 Feb 2025 13:11:58 -0800 Subject: [PATCH 082/102] Adds move-only callable support to filter Issue #89 --- cppitertools/filter.hpp | 4 +++- test/test_filter.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cppitertools/filter.hpp b/cppitertools/filter.hpp index 1e069070..53ed15cc 100644 --- a/cppitertools/filter.hpp +++ b/cppitertools/filter.hpp @@ -29,6 +29,8 @@ namespace iter { template class iter::impl::Filtered { + static_assert(!std::is_reference_v); + private: Container container_; mutable FilterFunc filter_func_; @@ -39,7 +41,7 @@ class iter::impl::Filtered { // Value constructor for use only in the filter function Filtered(FilterFunc filter_func, Container&& container) : container_(std::forward(container)), - filter_func_(filter_func) {} + filter_func_(std::move(filter_func)) {} public: Filtered(Filtered&&) = default; diff --git a/test/test_filter.cpp b/test/test_filter.cpp index a990844a..a0f5db96 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -25,6 +25,12 @@ TEST_CASE("filter: handles different callable types", "[filter]") { REQUIRE(v == vc); } + SECTION("with move-only callable object") { + auto f = filter(MoveOnlyLessThanValue{5}, ns); + Vec v(std::begin(f), std::end(f)); + REQUIRE(v == vc); + } + SECTION("with lambda") { auto ltf = [](int i) { return i < 5; }; auto f = filter(ltf, ns); From d45c47b75f28c976ae06cb472fcd29905b1bc207 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 16:30:40 -0800 Subject: [PATCH 083/102] Updates FnPartials to move stored_arg when rvalue Overloads `operator|` for rvalue and lvalue `Pipeable`s and downcasts accordingly. Adds rvalue-reference qualified overloads to FnPartial `operator|` that calls `std::move(stored_arg)` when creating the actual iterable object. This allows a move-only callable to get passed by value, but only moves, all the way into the itertool. ``` // both should work iter::some_itertool(sequence, MoveOnlyCallable{}); sequence | iter::some_itertool(MoveOnlyCallable{}); ``` I did attempt passing a `std::reference_wrapper` from the `FnPartial` but the iteration happens after the `FnPartial` is destroyed, So the following is unsafe: ``` // unsafe, wrong template auto operator()(Container&& container) const { if constexpr (std::is_copy_constructible_v) { return F{}(stored_arg, std::forward(container)); } else { return F{}(std::ref(stored_arg), std::forward(container)); } } ``` Setting the stage to fix #89 --- cppitertools/internal/iterbase.hpp | 38 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index 9a8b33ba..9cb97442 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -353,9 +353,14 @@ namespace iter { template struct Pipeable { template -#if defined(__GNUC__) && !defined(__clang__) - [[gnu::no_dangling]] +#if defined(__GNUC__) && !defined(__clang__) + [[gnu::no_dangling]] #endif + friend decltype(auto) operator|(T&& x, Pipeable&& p) { + return static_cast(p)(std::forward(x)); + } + + template friend decltype(auto) operator|(T&& x, const Pipeable& p) { return static_cast(p)(std::forward(x)); } @@ -378,11 +383,17 @@ namespace iter { protected: template struct FnPartial : Pipeable> { + static_assert(!std::is_reference_v); mutable T stored_arg; - constexpr FnPartial(T in_t) : stored_arg(in_t) {} + constexpr FnPartial(T in_t) : stored_arg(std::move(in_t)) {} template - auto operator()(Container&& container) const { + auto operator()(Container&& container) && { + return F{}(std::move(stored_arg), std::forward(container)); + } + + template + auto operator()(Container&& container) const& { return F{}(stored_arg, std::forward(container)); } }; @@ -403,10 +414,15 @@ namespace iter { template struct FnPartial : Pipeable> { mutable T stored_arg; - constexpr FnPartial(T in_t) : stored_arg(in_t) {} + constexpr FnPartial(T in_t) : stored_arg(std::move(in_t)) {} template - auto operator()(Container&& container) const { + auto operator()(Container&& container) && { + return F{}(std::forward(container), std::move(stored_arg)); + } + + template + auto operator()(Container&& container) const& { return F{}(std::forward(container), stored_arg); } }; @@ -469,10 +485,16 @@ namespace iter { template struct FnPartial : Pipeable> { mutable T stored_arg; - constexpr FnPartial(T in_t) : stored_arg(in_t) {} + constexpr FnPartial(T in_t) : stored_arg(std::move(in_t)) {} template - auto operator()(Container&& container) const { + auto operator()(Container&& container) && { + return IterToolFnOptionalBindSecond{}( + std::forward(container), std::move(stored_arg)); + } + + template + auto operator()(Container&& container) const& { return IterToolFnOptionalBindSecond{}( std::forward(container), stored_arg); } From dcc59d65c5036693c114f5a03b611b900180c83e Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 16:52:11 -0800 Subject: [PATCH 084/102] Uses unique_ptr in move-only callable for louder error Issue #89 --- test/helpers.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/helpers.hpp b/test/helpers.hpp index 94fd08f1..f94250f3 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -432,10 +433,12 @@ class LessThanValue { class MoveOnlyLessThanValue { private: - int compare_val; + // unique_ptr is better for triggering asan than an int if there's a dangling + // reference to the callable + std::unique_ptr compare_val; public: - MoveOnlyLessThanValue(int v) : compare_val(v) {} + MoveOnlyLessThanValue(int v) : compare_val{std::make_unique(v)} {} MoveOnlyLessThanValue(const MoveOnlyLessThanValue&) = delete; MoveOnlyLessThanValue& operator=(const MoveOnlyLessThanValue&) = delete; @@ -444,7 +447,7 @@ class MoveOnlyLessThanValue { MoveOnlyLessThanValue& operator=(MoveOnlyLessThanValue&&) = default; bool operator()(int i) { - return i < this->compare_val; + return i < *compare_val; } }; From 0fc1cf1a3d889da8a8d390faedf4e4dcfc68e437 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 16:53:13 -0800 Subject: [PATCH 085/102] Adds filter tests with pipe and move-only Issue #89 --- test/test_filter.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/test/test_filter.cpp b/test/test_filter.cpp index a0f5db96..445476c8 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -13,30 +13,46 @@ using Vec = const std::vector; TEST_CASE("filter: handles different callable types", "[filter]") { Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5}; Vec vc = {1, 2, 3, 1, -1}; + std::vector v; SECTION("with function pointer") { auto f = filter(less_than_five, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + v = Vec(std::begin(f), std::end(f)); } SECTION("with callable object") { auto f = filter(LessThanValue{5}, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + v = Vec(std::begin(f), std::end(f)); + } + + SECTION("with lvalue callable object") { + auto lt = LessThanValue{5}; + SECTION("normal call") { + auto f = filter(lt, ns); + v = Vec(std::begin(f), std::end(f)); + } + SECTION("pipe") { + auto f = ns | filter(lt); + v = Vec(std::begin(f), std::end(f)); + } } SECTION("with move-only callable object") { - auto f = filter(MoveOnlyLessThanValue{5}, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + SECTION("normal call") { + auto f = filter(MoveOnlyLessThanValue{5}, ns); + v = Vec(std::begin(f), std::end(f)); + } + SECTION("pipe") { + auto f = ns | filter(MoveOnlyLessThanValue{5}); + v = Vec(std::begin(f), std::end(f)); + } } SECTION("with lambda") { auto ltf = [](int i) { return i < 5; }; auto f = filter(ltf, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + v = Vec(std::begin(f), std::end(f)); } + REQUIRE(v == vc); } TEST_CASE("filter: handles pointer to member", "[filter]") { From 020f99db7570a39f281fcde6e59da154de58995d Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:04:21 -0800 Subject: [PATCH 086/102] Adds support for move-only callables in filterfalse Issue #89 --- cppitertools/filterfalse.hpp | 3 ++- test/test_filterfalse.cpp | 39 +++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cppitertools/filterfalse.hpp b/cppitertools/filterfalse.hpp index 4056c402..269724dd 100644 --- a/cppitertools/filterfalse.hpp +++ b/cppitertools/filterfalse.hpp @@ -48,7 +48,8 @@ class iter::impl::FilterFalsed friend FilterFalseFn; FilterFalsed(FilterFunc in_filter_func, Container&& in_container) : Filtered, Container>( - {in_filter_func}, std::forward(in_container)) {} + {std::move(in_filter_func)}, + std::forward(in_container)) {} }; #endif diff --git a/test/test_filterfalse.cpp b/test/test_filterfalse.cpp index 9a1dd4bc..ad79646a 100644 --- a/test/test_filterfalse.cpp +++ b/test/test_filterfalse.cpp @@ -13,31 +13,46 @@ using Vec = const std::vector; TEST_CASE("filterfalse: handles different callable types", "[filterfalse]") { Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5}; Vec vc = {5, 6, 7, 5}; + std::vector v; SECTION("with function pointer") { auto f = filterfalse(less_than_five, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + v = Vec(std::begin(f), std::end(f)); } SECTION("with callable object") { - std::vector v; - SECTION("Normal call") { - auto f = filterfalse(LessThanValue{5}, ns); - v.assign(std::begin(f), std::end(f)); + auto f = filterfalse(LessThanValue{5}, ns); + v = Vec(std::begin(f), std::end(f)); + } + + SECTION("with lvalue callable object") { + auto lt = LessThanValue{5}; + SECTION("normal call") { + auto f = filterfalse(lt, ns); + v = Vec(std::begin(f), std::end(f)); + } + SECTION("pipe") { + auto f = ns | filterfalse(lt); + v = Vec(std::begin(f), std::end(f)); } - SECTION("Pipe") { - auto f = ns | filterfalse(LessThanValue{5}); - v.assign(std::begin(f), std::end(f)); + } + + SECTION("with move-only callable object") { + SECTION("normal call") { + auto f = filterfalse(MoveOnlyLessThanValue{5}, ns); + v = Vec(std::begin(f), std::end(f)); + } + SECTION("pipe") { + auto f = ns | filterfalse(MoveOnlyLessThanValue{5}); + v = Vec(std::begin(f), std::end(f)); } - REQUIRE(v == vc); } SECTION("with lambda") { auto ltf = [](int i) { return i < 5; }; auto f = filterfalse(ltf, ns); - Vec v(std::begin(f), std::end(f)); - REQUIRE(v == vc); + v = Vec(std::begin(f), std::end(f)); } + REQUIRE(v == vc); } TEST_CASE("filterfalse: handles pointer to member", "[filterfalse]") { From f9f7de419c7360c97281cc35db02c3d689aa005b Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:04:52 -0800 Subject: [PATCH 087/102] Adds support for move-only callables in dropwhile Issue #89 --- cppitertools/dropwhile.hpp | 2 +- test/test_dropwhile.cpp | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/cppitertools/dropwhile.hpp b/cppitertools/dropwhile.hpp index e751bfe3..86942826 100644 --- a/cppitertools/dropwhile.hpp +++ b/cppitertools/dropwhile.hpp @@ -29,7 +29,7 @@ class iter::impl::Dropper { Dropper(FilterFunc filter_func, Container&& container) : container_(std::forward(container)), - filter_func_(filter_func) {} + filter_func_(std::move(filter_func)) {} public: Dropper(Dropper&&) = default; diff --git a/test/test_dropwhile.cpp b/test/test_dropwhile.cpp index 97c947f4..077bfd73 100644 --- a/test/test_dropwhile.cpp +++ b/test/test_dropwhile.cpp @@ -10,6 +10,51 @@ using iter::dropwhile; using Vec = const std::vector; +TEST_CASE("dropwhile: handles different callable types", "[dropwhile]") { + Vec ns = {1, 3, 4, 20, 2, 4, 6, 8}; + Vec vc = {20, 2, 4, 6, 8}; + std::vector v; + SECTION("with function pointer") { + auto d = dropwhile(less_than_five, ns); + v = Vec(std::begin(d), std::end(d)); + } + + SECTION("with callable object") { + auto d = dropwhile(LessThanValue{5}, ns); + v = Vec(std::begin(d), std::end(d)); + } + + SECTION("with lvalue callable object") { + auto lt = LessThanValue{5}; + SECTION("normal call") { + auto d = dropwhile(lt, ns); + v = Vec(std::begin(d), std::end(d)); + } + SECTION("pipe") { + auto d = ns | dropwhile(lt); + v = Vec(std::begin(d), std::end(d)); + } + } + + SECTION("with move-only callable object") { + SECTION("normal call") { + auto d = dropwhile(MoveOnlyLessThanValue{5}, ns); + v = Vec(std::begin(d), std::end(d)); + } + SECTION("pipe") { + auto d = ns | dropwhile(MoveOnlyLessThanValue{5}); + v = Vec(std::begin(d), std::end(d)); + } + } + + SECTION("with lambda") { + auto ltf = [](int i) { return i < 5; }; + auto d = dropwhile(ltf, ns); + v = Vec(std::begin(d), std::end(d)); + } + REQUIRE(v == vc); +} + TEST_CASE("dropwhile: skips initial elements", "[dropwhile]") { Vec ns{1, 2, 3, 4, 5, 6, 7, 8}; std::vector v; From 35fdf0c9fba07476d4d07f608396640d6d6d2c47 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:05:23 -0800 Subject: [PATCH 088/102] Adds support for move-only callables to groupby Issue #89 --- cppitertools/groupby.hpp | 3 +- test/test_groupby.cpp | 74 +++++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/cppitertools/groupby.hpp b/cppitertools/groupby.hpp index 6d7453f9..313f088a 100644 --- a/cppitertools/groupby.hpp +++ b/cppitertools/groupby.hpp @@ -35,7 +35,8 @@ class iter::impl::GroupProducer { using key_func_ret = std::invoke_result_t>; GroupProducer(Container&& container, KeyFunc key_func) - : container_(std::forward(container)), key_func_(key_func) {} + : container_(std::forward(container)), + key_func_(std::move(key_func)) {} public: GroupProducer(GroupProducer&&) = default; diff --git a/test/test_groupby.cpp b/test/test_groupby.cpp index 6ce9d479..5904c78d 100644 --- a/test/test_groupby.cpp +++ b/test/test_groupby.cpp @@ -3,6 +3,7 @@ #include "helpers.hpp" #include +#include #include #include @@ -21,22 +22,38 @@ namespace { } }; + struct MoveOnlySizer { + // here to trigger asan if a dangling reference gets used + std::unique_ptr counter_ = std::make_unique(); + + MoveOnlySizer(const MoveOnlySizer&) = delete; + MoveOnlySizer& operator=(const MoveOnlySizer&) = delete; + + MoveOnlySizer(MoveOnlySizer&&) = default; + MoveOnlySizer& operator=(MoveOnlySizer&&) = default; + + int operator()(const std::string& s) { + ++*counter_; + return s.size(); + } + }; + const std::vector vec = { "hi", "ab", "ho", "abc", "def", "abcde", "efghi"}; } -TEST_CASE("groupby: works with lambda, callable, and function pointer") { +TEST_CASE("groupby: handles different callable types", "[groupby]") { std::vector keys; std::vector> groups; - SECTION("Function pointer") { - SECTION("Normal call") { + SECTION("with function pointer") { + SECTION("normal call") { for (auto&& gb : groupby(vec, length)) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); } } - SECTION("Pipe") { + SECTION("pipe") { for (auto&& gb : vec | groupby(length)) { keys.push_back(gb.first); groups.emplace_back(std::begin(gb.second), std::end(gb.second)); @@ -44,14 +61,53 @@ TEST_CASE("groupby: works with lambda, callable, and function pointer") { } } - SECTION("Callable object") { - for (auto&& gb : groupby(vec, Sizer{})) { - keys.push_back(gb.first); - groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + SECTION("with callable object") { + SECTION("normal call") { + for (auto&& gb : groupby(vec, Sizer{})) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } + } + SECTION("pipe") { + for (auto&& gb : vec | groupby(Sizer{})) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } } } - SECTION("lambda function") { + SECTION("with lvalue callable object") { + auto sizer = Sizer{}; + SECTION("normal call") { + for (auto&& gb : groupby(vec, sizer)) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } + } + SECTION("pipe") { + for (auto&& gb : vec | groupby(sizer)) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } + } + } + + SECTION("with move-only callable object") { + SECTION("normal call") { + for (auto&& gb : groupby(vec, MoveOnlySizer{})) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } + } + SECTION("pipe") { + for (auto&& gb : vec | groupby(MoveOnlySizer{})) { + keys.push_back(gb.first); + groups.emplace_back(std::begin(gb.second), std::end(gb.second)); + } + } + } + + SECTION("with lambda") { for (auto&& gb : groupby(vec, [](const std::string& s) { return s.size(); })) { keys.push_back(gb.first); From 417b1ebe067a381585def401c2f94a8c1ae20476 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:06:29 -0800 Subject: [PATCH 089/102] Adds support for move-only callables to imap Issue #89 --- cppitertools/imap.hpp | 3 +- test/test_imap.cpp | 72 ++++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/cppitertools/imap.hpp b/cppitertools/imap.hpp index 80b8cc3d..21ca2e81 100644 --- a/cppitertools/imap.hpp +++ b/cppitertools/imap.hpp @@ -16,7 +16,8 @@ namespace iter { // See #66 -> StarMapper(containers)...))> { - return starmap(map_func, zip(std::forward(containers)...)); + return starmap( + std::move(map_func), zip(std::forward(containers)...)); } using PipeableAndBindFirst::operator(); }; diff --git a/test/test_imap.cpp b/test/test_imap.cpp index 40a9d3e3..cfda992b 100644 --- a/test/test_imap.cpp +++ b/test/test_imap.cpp @@ -17,10 +17,29 @@ namespace { return i + 1; } - class PlusOner { + struct PlusOner { + int operator()(int i) const { + return i + 1; + } + }; + + class MoveOnlyAdder { + private: + // unique_ptr is better for triggering asan than an int if there's a + // dangling reference to the callable + std::unique_ptr add_amount_; + public: + MoveOnlyAdder(int v) : add_amount_{std::make_unique(v)} {} + + MoveOnlyAdder(const MoveOnlyAdder&) = delete; + MoveOnlyAdder& operator=(const MoveOnlyAdder&) = delete; + + MoveOnlyAdder(MoveOnlyAdder&&) = default; + MoveOnlyAdder& operator=(MoveOnlyAdder&&) = default; + int operator()(int i) { - return i + 1; + return i + *add_amount_; } }; @@ -33,31 +52,48 @@ namespace { } } -TEST_CASE("imap: works with lambda, callable, and function", "[imap]") { - Vec ns = {10, 20, 30}; +TEST_CASE("imap: handles different callable types", "[imap]") { + Vec ns = {10, 15, 300}; + Vec vc = {11, 16, 301}; std::vector v; - SECTION("with lambda") { - auto im = imap([](int i) { return i + 1; }, ns); - v.assign(std::begin(im), std::end(im)); + SECTION("with function pointer") { + auto m = imap(plusone, ns); + v = Vec(std::begin(m), std::end(m)); } - SECTION("with callable") { - SECTION("Normal call") { - auto im = imap(PlusOner{}, ns); - v.assign(std::begin(im), std::end(im)); + SECTION("with callable object") { + auto m = imap(PlusOner{}, ns); + v = Vec(std::begin(m), std::end(m)); + } + + SECTION("with lvalue callable object") { + auto lt = PlusOner{}; + SECTION("normal call") { + auto m = imap(lt, ns); + v = Vec(std::begin(m), std::end(m)); } - SECTION("Pipe") { - auto im = ns | imap(PlusOner{}); - v.assign(std::begin(im), std::end(im)); + SECTION("pipe") { + auto m = ns | imap(lt); + v = Vec(std::begin(m), std::end(m)); } } - SECTION("with function") { - auto im = imap(PlusOner{}, ns); - v.assign(std::begin(im), std::end(im)); + SECTION("with move-only callable object") { + SECTION("normal call") { + auto m = imap(MoveOnlyAdder{1}, ns); + v = Vec(std::begin(m), std::end(m)); + } + SECTION("pipe") { + auto m = ns | imap(MoveOnlyAdder{1}); + v = Vec(std::begin(m), std::end(m)); + } } - Vec vc = {11, 21, 31}; + SECTION("with lambda") { + auto ltf = [](int i) { return i + 1; }; + auto m = imap(ltf, ns); + v = Vec(std::begin(m), std::end(m)); + } REQUIRE(v == vc); } From 6a23fdae0ac40524a53ffa3d6a72689fb60aa990 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:07:08 -0800 Subject: [PATCH 090/102] Adds tests for move-only callables to starmap Issue #89 --- test/test_starmap.cpp | 81 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/test/test_starmap.cpp b/test/test_starmap.cpp index e3958c84..c8758669 100644 --- a/test/test_starmap.cpp +++ b/test/test_starmap.cpp @@ -47,16 +47,42 @@ namespace { return a; } }; + + struct Adder { + long operator()(long a, int b) { + return a + b; + } + }; + + struct MoveOnlyAddAndPlus { + private: + // unique_ptr is better for triggering asan than an int if there's a + // dangling reference to the callable + std::unique_ptr add_amount_; + + public: + MoveOnlyAddAndPlus(int v) : add_amount_{std::make_unique(v)} {} + + MoveOnlyAddAndPlus(const MoveOnlyAddAndPlus&) = delete; + MoveOnlyAddAndPlus& operator=(const MoveOnlyAddAndPlus&) = delete; + + MoveOnlyAddAndPlus(MoveOnlyAddAndPlus&&) = default; + MoveOnlyAddAndPlus& operator=(MoveOnlyAddAndPlus&&) = default; + + int operator()(long a, int b) { + return a + b + *add_amount_; + } + }; } TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") { - using Vec = const std::vector; const std::vector> v1 = {{1l, 2}, {3l, 11}, {6l, 7}}; - Vec vc = {2l, 33l, 42l}; + const std::vector greater_vc = {2l, 33l, 42l}; + const std::vector added_vc = {3l, 14l, 13l}; - std::vector v; - SECTION("with function") { - SECTION("Normal call") { + SECTION("with function pointer") { + std::vector v; + SECTION("normal call") { auto sm = starmap(f, v1); v.assign(std::begin(sm), std::end(sm)); } @@ -64,13 +90,54 @@ TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") { auto sm = v1 | starmap(f); v.assign(std::begin(sm), std::end(sm)); } + REQUIRE(v == greater_vc); + } + + SECTION("with callable object") { + std::vector v; + SECTION("normal call") { + auto sm = starmap(Adder{}, v1); + v.assign(std::begin(sm), std::end(sm)); + } + SECTION("pipe") { + auto sm = v1 | starmap(Adder{}); + v.assign(std::begin(sm), std::end(sm)); + } + REQUIRE(v == added_vc); + } + + SECTION("with lvalue callable object") { + std::vector v; + auto adder = Adder{}; + SECTION("normal call") { + auto sm = starmap(adder, v1); + v.assign(std::begin(sm), std::end(sm)); + } + SECTION("pipe") { + auto sm = v1 | starmap(adder); + v.assign(std::begin(sm), std::end(sm)); + } + REQUIRE(v == std::vector{3l, 14l, 13l}); + } + + SECTION("with move-only callable object") { + const std::vector sum_plus_one_vc = {4l, 14l, 13l}; + std::vector v; + SECTION("normal call") { + auto m = starmap(MoveOnlyAddAndPlus{1}, v1); + v.assign(std::begin(m), std::end(m)); + } + SECTION("pipe") { + auto m = v1 | starmap(MoveOnlyAddAndPlus{1}); + v.assign(std::begin(m), std::end(m)); + } } SECTION("with lambda") { auto sm = starmap([](long a, int b) { return a * b; }, v1); - v.assign(std::begin(sm), std::end(sm)); + std::vector v(std::begin(sm), std::end(sm)); + REQUIRE(v == greater_vc); } - REQUIRE(v == vc); } TEST_CASE("starmap: works with pointer to member function", "[starmap]") { From 7242a9aadbbd70026a5e3b4059df9075fe465a7f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:07:54 -0800 Subject: [PATCH 091/102] Adds support for move-only callables to takewhile Issue #89 --- cppitertools/takewhile.hpp | 2 +- test/test_takewhile.cpp | 53 ++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/cppitertools/takewhile.hpp b/cppitertools/takewhile.hpp index 338e43aa..3a351d0c 100644 --- a/cppitertools/takewhile.hpp +++ b/cppitertools/takewhile.hpp @@ -29,7 +29,7 @@ class iter::impl::Taker { Taker(FilterFunc filter_func, Container&& container) : container_(std::forward(container)), - filter_func_(filter_func) {} + filter_func_(std::move(filter_func)) {} public: Taker(Taker&&) = default; diff --git a/test/test_takewhile.cpp b/test/test_takewhile.cpp index 0be43f84..f562b438 100644 --- a/test/test_takewhile.cpp +++ b/test/test_takewhile.cpp @@ -10,36 +10,49 @@ using iter::takewhile; using Vec = const std::vector; -TEST_CASE("takewhile: works with lambda, callable, and function pointer", - "[takewhile]") { +TEST_CASE("takewhile: handles different callable types", "[takewhile]") { Vec ns = {1, 3, 4, 20, 2, 4, 6, 8}; - const Vec vc = {1, 3, 4}; - SECTION("function pointer") { + Vec vc = {1, 3, 4}; + std::vector v; + SECTION("with function pointer") { auto tw = takewhile(less_than_five, ns); - Vec v(std::begin(tw), std::end(tw)); - REQUIRE(v == vc); + v = Vec(std::begin(tw), std::end(tw)); } - SECTION("callable object") { - std::vector v; - SECTION("Normal call") { - auto tw = takewhile(LessThanValue{10}, ns); - v.assign(std::begin(tw), std::end(tw)); - } + SECTION("with callable object") { + auto tw = takewhile(LessThanValue{5}, ns); + v = Vec(std::begin(tw), std::end(tw)); + } - SECTION("Pipe") { - auto tw = ns | takewhile(LessThanValue{10}); - v.assign(std::begin(tw), std::end(tw)); + SECTION("with lvalue callable object") { + auto lt = LessThanValue{5}; + SECTION("normal call") { + auto tw = takewhile(lt, ns); + v = Vec(std::begin(tw), std::end(tw)); + } + SECTION("pipe") { + auto tw = ns | takewhile(lt); + v = Vec(std::begin(tw), std::end(tw)); } + } - REQUIRE(v == vc); + SECTION("with move-only callable object") { + SECTION("normal call") { + auto tw = takewhile(MoveOnlyLessThanValue{5}, ns); + v = Vec(std::begin(tw), std::end(tw)); + } + SECTION("pipe") { + auto tw = ns | takewhile(MoveOnlyLessThanValue{5}); + v = Vec(std::begin(tw), std::end(tw)); + } } - SECTION("lambda") { - auto tw = takewhile([](int i) { return i < 10; }, ns); - Vec v(std::begin(tw), std::end(tw)); - REQUIRE(v == vc); + SECTION("with lambda") { + auto ltf = [](int i) { return i < 5; }; + auto tw = takewhile(ltf, ns); + v = Vec(std::begin(tw), std::end(tw)); } + REQUIRE(v == vc); } TEST_CASE("takewhile: handles pointer to member", "[takewhile]") { From 051d8f9830c401697213dc538477d41c5709b213 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:08:09 -0800 Subject: [PATCH 092/102] Adds support for move-only callables to unique_justseen Issue #89 --- cppitertools/unique_justseen.hpp | 8 ++++--- test/test_unique_justseen.cpp | 40 +++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/cppitertools/unique_justseen.hpp b/cppitertools/unique_justseen.hpp index c4ca47f5..e86d20ba 100644 --- a/cppitertools/unique_justseen.hpp +++ b/cppitertools/unique_justseen.hpp @@ -9,9 +9,11 @@ namespace iter { namespace impl { - struct UniqueJustseenFn : PipeableAndBindOptionalSecond { + struct UniqueJustseenFn + : PipeableAndBindOptionalSecond { public: - using PipeableAndBindOptionalSecond::operator(); + using PipeableAndBindOptionalSecond:: + operator(); template auto operator()(Container&& container, KeyFunc key_fn) const { // decltype(auto) return type in lambda so reference types are preserved @@ -19,7 +21,7 @@ namespace iter { [](auto&& group) -> decltype(auto) { return *get_begin(group.second); }, - groupby(std::forward(container), key_fn)); + groupby(std::forward(container), std::move(key_fn))); } }; } diff --git a/test/test_unique_justseen.cpp b/test/test_unique_justseen.cpp index 6147b477..530ab869 100644 --- a/test/test_unique_justseen.cpp +++ b/test/test_unique_justseen.cpp @@ -102,21 +102,45 @@ struct IntWrapperKey { } }; -TEST_CASE("unique_justseen: works with key function", - "[unique_justseen]") { +struct MoveOnlyIntWrapperKey { + MoveOnlyIntWrapperKey(const MoveOnlyIntWrapperKey&) = delete; + MoveOnlyIntWrapperKey& operator=(const MoveOnlyIntWrapperKey&) = delete; + + MoveOnlyIntWrapperKey(MoveOnlyIntWrapperKey&&) = default; + MoveOnlyIntWrapperKey& operator=(MoveOnlyIntWrapperKey&&) = default; + int operator()(const IntWrapper& iw) const { + return iw.n; + } +}; + +TEST_CASE("unique_justseen: works with key function", "[unique_justseen]") { std::vector iwv = { {2}, {3}, {4}, {2}, {10}, {2}, {2}, {12}, {10}}; Vec vc{2, 3, 4, 2, 10, 2, 12, 10}; std::vector v; - SECTION("Normal call") { - for (auto&& iw : unique_justseen(iwv, IntWrapperKey{})) { - v.push_back(iw.n); + SECTION("with callable") { + SECTION("Normal call") { + for (auto&& iw : unique_justseen(iwv, IntWrapperKey{})) { + v.push_back(iw.n); + } + } + SECTION("Pipe") { + for (auto&& iw : iwv | unique_justseen(IntWrapperKey{})) { + v.push_back(iw.n); + } } } - SECTION("Pipe") { - for (auto&& iw : iwv | unique_justseen(IntWrapperKey{})) { - v.push_back(iw.n); + SECTION("with move-only callable") { + SECTION("Normal call") { + for (auto&& iw : unique_justseen(iwv, MoveOnlyIntWrapperKey{})) { + v.push_back(iw.n); + } + } + SECTION("Pipe") { + for (auto&& iw : iwv | unique_justseen(MoveOnlyIntWrapperKey{})) { + v.push_back(iw.n); + } } } From 7ce67a6e4602651130bab0b71bb31d877c5a66f4 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Wed, 12 Feb 2025 17:48:27 -0800 Subject: [PATCH 093/102] Replaces true/false tag dispatches with if constexpr --- cppitertools/internal/iterbase.hpp | 48 ++++++++++-------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index 9cb97442..34abd934 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -214,28 +214,20 @@ namespace iter { } } - template - void dumb_advance_impl( - Iter& iter, const EndIter& end, Distance distance, std::false_type) { - for (Distance i(0); i < distance && iter != end; ++i) { - ++iter; - } - } - - template - void dumb_advance_impl( - Iter& iter, const EndIter& end, Distance distance, std::true_type) { - if (static_cast(end - iter) < distance) { - iter = end; - } else { - iter += distance; - } - } - // iter will not be incremented past end template void dumb_advance(Iter& iter, const EndIter& end, Distance distance) { - dumb_advance_impl(iter, end, distance, is_random_access_iter{}); + if constexpr (is_random_access_iter{}) { + if (static_cast(end - iter) < distance) { + iter = end; + } else { + iter += distance; + } + } else { + for (Distance i(0); i < distance && iter != end; ++i) { + ++iter; + } + } } template @@ -452,22 +444,14 @@ namespace iter { using Base = PipeableAndBindFirst>; - protected: - template - auto operator()(Container&& container, std::false_type) const { - return static_cast(*this)( - std::forward(container)); - } - - template - auto operator()(Container&& container, std::true_type) const { - return (*this)(DefaultT{}, std::forward(container)); - } - public: template auto operator()(T&& t) const { - return (*this)(std::forward(t), IsIterable{}); + if constexpr (IsIterable{}) { + return (*this)(DefaultT{}, std::forward(t)); + } else { + return static_cast(*this)(std::forward(t)); + } } template Date: Thu, 13 Feb 2025 16:36:06 -0800 Subject: [PATCH 094/102] Sets bazelversion to 8.* --- .bazelversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bazelversion b/.bazelversion index cd1d2e94..f4abeaad 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -8.0.1 +8.* From da33b531264fe1604871c8a2765f2ef3dcd98d99 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 14 Feb 2025 14:39:17 -0800 Subject: [PATCH 095/102] Forwards key to `Group` If we knew the key type was not a reference, we could just call std::move, but there might be an lvalue reference hiding in there. --- cppitertools/groupby.hpp | 8 ++++++-- test/test_groupby.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cppitertools/groupby.hpp b/cppitertools/groupby.hpp index 313f088a..1c73f9f6 100644 --- a/cppitertools/groupby.hpp +++ b/cppitertools/groupby.hpp @@ -178,6 +178,8 @@ class iter::impl::GroupProducer { friend class Iterator; friend class GroupIterator; Iterator& owner_; + // The key function may return a reference, so we need to call forward, not + // move, when going for efficiency. key_func_ret key_; // completed is set if a Group is iterated through @@ -192,7 +194,7 @@ class iter::impl::GroupProducer { bool completed = false; Group(Iterator& owner, key_func_ret key) - : owner_(owner), key_(key) {} + : owner_(owner), key_(std::forward>(key)) {} public: ~Group() { @@ -204,7 +206,9 @@ class iter::impl::GroupProducer { // move-constructible, non-copy-constructible, non-assignable Group(Group&& other) noexcept - : owner_(other.owner_), key_{other.key_}, completed{other.completed} { + : owner_(other.owner_), + key_{std::forward>(other.key_)}, + completed{other.completed} { other.completed = true; } diff --git a/test/test_groupby.cpp b/test/test_groupby.cpp index 5904c78d..02798f39 100644 --- a/test/test_groupby.cpp +++ b/test/test_groupby.cpp @@ -40,6 +40,43 @@ namespace { const std::vector vec = { "hi", "ab", "ho", "abc", "def", "abcde", "efghi"}; + + struct Person { + std::string name; + int id; + bool operator==(const Person& other) const { + return id == other.id; + } + }; + + std::string& get_name(Person& p) { + return p.name; + } + + template + std::vector extract_person_group(G g) { + return {std::begin(g), std::end(g)}; + } +} + +TEST_CASE("groupby: handle key function that returns reference", "[groupby]") { + std::vector people = {{"first", 1}, {"first", 2}, {"first", 3}}; + std::vector keys; + std::vector> groups; + + for (auto&& gb : groupby(people, get_name)) { + groups.push_back(extract_person_group(std::move(gb.second))); + keys.push_back(gb.first); + } + + const std::vector kc = {"first"}; + const std::vector> gc = { + {{"first", 1}, {"first", 2}, {"first", 3}}}; + + REQUIRE(people[0].name == "first"); + REQUIRE(gc[0][0].name == "first"); + REQUIRE(keys == kc); + REQUIRE(groups == gc); } TEST_CASE("groupby: handles different callable types", "[groupby]") { From cf16b06cee79c7216233448cd25e4cb925efd7d2 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Fri, 14 Feb 2025 14:47:19 -0800 Subject: [PATCH 096/102] Only uses gnu::nodangling when it's available in gcc --- cppitertools/internal/iterbase.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index 34abd934..a21489dc 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -345,7 +345,7 @@ namespace iter { template struct Pipeable { template -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 14 [[gnu::no_dangling]] #endif friend decltype(auto) operator|(T&& x, Pipeable&& p) { From 5bc867a1ee8fbcee322f072d700171e1901214dc Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 18 Feb 2025 14:02:50 -0800 Subject: [PATCH 097/102] Removes cv qualifiers from value_type --- cppitertools/internal/iterbase.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index a21489dc..f4d8792e 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -117,7 +117,7 @@ namespace iter { template using iterator_traits_deref = - std::remove_reference_t>; + std::remove_cv_t>>; template struct IsIterable : std::false_type {}; From 392734a2991980f25fe18c91d8bee05c99477d0f Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 18 Feb 2025 17:03:48 -0800 Subject: [PATCH 098/102] Removes cv qualifiers from iterator iterator value_type --- cppitertools/internal/iteratoriterator.hpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cppitertools/internal/iteratoriterator.hpp b/cppitertools/internal/iteratoriterator.hpp index 3993a4d0..0ab23bad 100644 --- a/cppitertools/internal/iteratoriterator.hpp +++ b/cppitertools/internal/iteratoriterator.hpp @@ -24,7 +24,8 @@ namespace iter { template class IteratorIterator { - template friend class IteratorIterator; + template + friend class IteratorIterator; using Diff = std::ptrdiff_t; static_assert( std::is_same< @@ -37,10 +38,14 @@ namespace iter { public: using iterator_category = std::random_access_iterator_tag; - using value_type = std::remove_reference_t())>; + using value_type = std::remove_cv_t< + std::remove_reference_t())>>; using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; + using pointer = + std::remove_reference_t())>*; + using reference = std::add_lvalue_reference_t< + std::remove_reference_t())>>; + IteratorIterator() = default; IteratorIterator(const TopIter& it) : sub_iter{it} {} @@ -84,7 +89,7 @@ namespace iter { return **this->sub_iter; } - auto operator-> () const -> decltype(*sub_iter) { + auto operator->() const -> decltype(*sub_iter) { return *this->sub_iter; } From 1bbe5a437f5039d242b0fb6983faa2a3f005edc5 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 18 Feb 2025 17:10:03 -0800 Subject: [PATCH 099/102] Removes cv qualifiers from DerefHolder when storing value --- cppitertools/internal/iterbase.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cppitertools/internal/iterbase.hpp b/cppitertools/internal/iterbase.hpp index f4d8792e..e9627079 100644 --- a/cppitertools/internal/iterbase.hpp +++ b/cppitertools/internal/iterbase.hpp @@ -262,9 +262,8 @@ namespace iter { std::is_same::value && are_same::value> {}; // DerefHolder holds the value gotten from an iterator dereference - // if the iterate dereferences to an lvalue references, a pointer to the - // element is stored - // if it does not, a value is stored instead + // if the iterator dereferences to an lvalue references, a pointer to the + // element is stored. if it does not, a value is stored instead // get() returns a reference to the held item // get_ptr() returns a pointer to the held item // reset() replaces the currently held item @@ -274,7 +273,7 @@ namespace iter { static_assert(!std::is_lvalue_reference::value, "Non-lvalue-ref specialization used for lvalue ref type"); // it could still be an rvalue reference - using TPlain = std::remove_reference_t; + using TPlain = std::remove_cv_t>; std::optional item_p_; From 674e8b9a6d2f2319ea38312b6ada073937843e70 Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 18 Feb 2025 17:10:52 -0800 Subject: [PATCH 100/102] Specifies pointer and reference to match operators exactly --- cppitertools/filter.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cppitertools/filter.hpp b/cppitertools/filter.hpp index 53ed15cc..d743d8b1 100644 --- a/cppitertools/filter.hpp +++ b/cppitertools/filter.hpp @@ -86,8 +86,8 @@ class iter::impl::Filtered { using iterator_category = std::input_iterator_tag; using value_type = iterator_traits_deref; using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; + using pointer = typename Holder::pointer; + using reference = typename Holder::reference; Iterator(IteratorWrapper&& sub_iter, IteratorWrapper&& sub_end, FilterFunc& filter_func) From 13947eb68f464cb014343de7a155016290655d1a Mon Sep 17 00:00:00 2001 From: Ryan Haining Date: Tue, 18 Feb 2025 17:13:09 -0800 Subject: [PATCH 101/102] Specifies chain pointer and reference to match calls exactly --- cppitertools/chain.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cppitertools/chain.hpp b/cppitertools/chain.hpp index 76759eeb..05557387 100644 --- a/cppitertools/chain.hpp +++ b/cppitertools/chain.hpp @@ -142,8 +142,8 @@ class iter::impl::Chained { using iterator_category = std::input_iterator_tag; using value_type = typename IteratorData::TraitsValue; using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; + using pointer = typename IteratorData::ArrowType; + using reference = typename IteratorData::DerefType; Iterator(std::size_t i, typename IterData::IterTupType&& iters, typename IterData::IterTupType&& ends) From bb59879a7dbea29cc70d204b8c4889e35bb1110e Mon Sep 17 00:00:00 2001 From: Cristi Popa Date: Sat, 6 Dec 2025 15:55:05 +0100 Subject: [PATCH 102/102] Update readme to use google style markdown rendering --- README.md | 382 +++++++++++++++++++++++++++++------------------------- 1 file changed, 206 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index 7b43c218..740dacbd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ - - CPPItertools ============ Range-based for loop add-ons inspired by the Python builtins and itertools -library. Like itertools and the Python3 builtins, this library uses lazy +library. Like itertools and the Python3 builtins, this library uses lazy evaluation wherever possible. *Note*: Everything is inside the `iter` namespace. @@ -44,7 +42,7 @@ Status | Compilers [chunked](#chunked)
[batched](#batched)
-##### Combinatoric fuctions +##### Combinatorial functions [product](#product)
[combinations](#combinations)
[combinations\_with\_replacement](#combinations_with_replacement)
@@ -55,11 +53,10 @@ Status | Compilers This library is **header-only** and relies only on the C++ standard library. The only exception is `zip_longest` which uses `boost::optional`. `#include ` will include all of the provided -tools except for `zip_longest` which must be included separately. You may +tools except for `zip_longest` which must be included separately. You may also include individual pieces with the relevant header (`#include ` for example). - ### Running tests You may use either `scons` or `bazel` to build the tests. `scons` seems to work better with viewing the test output, but the same `bazel` command @@ -86,7 +83,7 @@ $ bazel test //test:test_enumerate # runs a specific test #### Requirements of passed objects Most itertools will work with iterables using InputIterators and not copy -or move any underlying elements. The itertools that need ForwardIterators or +or move any underlying elements. The itertools that need ForwardIterators or have additional requirements are noted in this document. However, the cases should be fairly obvious: any time an element needs to appear multiple times (as in `combinations` or `cycle`) or be looked at more than once (specifically, @@ -110,7 +107,7 @@ appropriate as a GitHub issue (or you just don't want to open one), you can email me directly with whatever code you have that describes the problem; I've been pretty responsive in the past. If I believe you are "misusing" the library, I'll try to put the blame on myself for being unclear -in this document and take the steps to clarify it. So please, contact me with +in this document and take the steps to clarify it. So please, contact me with any concerns, I'm open to feedback. #### How (not) to use this library @@ -125,41 +122,42 @@ know. #### Handling of rvalues vs lvalues The rules are pretty simple, and the library can be largely used without -knowledge of them. -Let's take an example +knowledge of them. Let's take an example + ```c++ std::vector vec{2,4,6,8}; for (auto&& p : enumerate(vec)) { /* ... */ } ``` + In this case, `enumerate` will return an object that has bound a reference to `vec`. No copies are produced here, neither of `vec` nor of the elements it holds. If an rvalue was passed to enumerate, binding a reference would be unsafe. Consider: + ```c++ for (auto&& p : enumerate(std::vector{2,4,6,8})) { /* ... */ } ``` + Instead, `enumerate` will return an object that has the temporary *moved* into -it. That is, the returned object will contain a `std::vector` rather than +it. That is, the returned object will contain a `std::vector` rather than just a reference to one. This may seem like a contrived example, but it matters when `enumerate` is passed the result of a function call like `enumerate(f())`, -or, more obviously, something like `enumerate(zip(a, b))`. The object returned +or, more obviously, something like `enumerate(zip(a, b))`. The object returned from `zip` must be moved into the `enumerate` object. As a more specific result, itertools can be mixed and nested. - - #### Pipe syntax Wherever it makes sense, I've implemented the "pipe" operator that has become common in similar libraries. When the syntax is available, it is done by pulling out the iterable from the call and placing it before the tool. For example: ```c++ -filter(pred, seq); // regular call -seq | filter(pred); // pipe-style -enumerate(seq); // regular call -seq | enumerate; // pipe-style. +filter(pred, seq); // regular call +seq | filter(pred); // pipe-style +enumerate(seq); // regular call +seq | enumerate; // pipe-style. ``` The following tools support pipe. The remaining I left out because although @@ -193,46 +191,50 @@ would expect them to behave: I don't personally care for the piping style, but it seemed to be desired by the users. - range ----- Uses an underlying iterator to achieve the same effect of the python range -function. `range` can be used in three different ways: +function. `range` can be used in three different ways: + +Only the stopping point is provided. Prints `0 1 2 3 4 5 6 7 8 9` -Only the stopping point is provided. Prints `0 1 2 3 4 5 6 7 8 9` ```c++ for (auto i : range(10)) { - cout << i << '\n'; + cout << i << '\n'; } ``` -The start and stop are both provided. Prints `10 11 12 13 14` +The start and stop are both provided. Prints `10 11 12 13 14` + ```c++ for (auto i : range(10, 15)) { - cout << i << '\n'; + cout << i << '\n'; } ``` -The start, stop, and step are all provided. Prints `20 22 24 26 28` +The start, stop, and step are all provided. Prints `20 22 24 26 28` + ```c++ for (auto i : range(20, 30, 2)) { - cout << i << '\n'; + cout << i << '\n'; } ``` -Negative values are allowed as well. Prints `2 1 0 -1 -2` +Negative values are allowed as well. Prints `2 1 0 -1 -2` + ```c++ for (auto i : range(2, -3, -1)) { - cout << i << '\n'; + cout << i << '\n'; } ``` A step size of 0 results in an empty range (Python's raises an exception). The following prints nothing + ```c++ for (auto i : range(0, 10, 0)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -240,9 +242,10 @@ In addition to normal integer range operations, doubles and other numeric types are supported through the template Prints: `5.0 5.5 6.0` ... `9.5` + ```c++ for(auto i : range(5.0, 10.0, 0.5)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -253,46 +256,45 @@ recomputed at each step to avoid accumulating floating point inaccuracies slower but more accurate. `range` also supports the following operations: - - `.size()` to get the number of elements in the range (not enabled for - floating point ranges). - - Accessors for `.start()`, `.stop()`, and `.step()`. - - Indexing. Given a range `r`, `r[n]` is the `n`th element in the range. + - `.size()` to get the number of elements in the range (not enabled for + floating point ranges). + - Accessors for `.start()`, `.stop()`, and `.step()`. + - Indexing. Given a range `r`, `r[n]` is the `n`th element in the range. enumerate --------- - -Continually "yields" containers similar to pairs. They are structs with -the index in `.first`, and the element in `.second`, and also work with structured -binding declarations. -Usage appears as: +Continually "yields" containers similar to pairs. They are structs with the +index in `.first`, and the element in `.second`, and also work with structured +binding declarations. Usage appears as: ```c++ vector vec{2, 4, 6, 8}; for (auto&& [i, e] : enumerate(vec)) { - cout << i << ": " << e << '\n'; + cout << i << ": " << e << '\n'; } ``` filter ------ -Called as `filter(predicate, iterable)`. The predicate can be any callable. +Called as `filter(predicate, iterable)`. The predicate can be any callable. `filter` will only yield values that are true under the predicate. -Prints values greater than 4: `5 6 7 8` +Prints values greater than 4: `5 6 7 8` + ```c++ vector vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1}; for (auto&& i : filter([] (int i) { return i > 4; }, vec)) { - cout << i <<'\n'; + cout << i <<'\n'; } - ``` If no predicate is passed, the elements themselves are tested for truth Prints only non-zero values. + ```c++ for(auto&& i : filter(vec)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -300,36 +302,38 @@ filterfalse ----------- Similar to filter, but only prints values that are false under the predicate. -Prints values not greater than 4: `1 4 3 2 3 2 1 ` +Prints values not greater than 4: `1 4 3 2 3 2 1` + ```c++ vector vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1}; for (auto&& i : filterfalse([] (int i) { return i > 4; }, vec)) { - cout << i <<'\n'; + cout << i <<'\n'; } - ``` If no predicate is passed, the elements themselves are tested for truth. Prints only zero values. + ```c++ for(auto&& i : filterfalse(vec)) { - cout << i << '\n'; + cout << i << '\n'; } - ``` + unique\_everseen ---------------- +---------------- *Additional Requirements*: Underlying values must be copy-constructible. This is a filter adaptor that only generates values that have never been seen before. Prints `1 2 3 4 5 6 7 8 9` + ```c++ vector v {1,2,3,4,3,2,1,5,6,7,7,8,9,8,9,6}; for (auto&& i : unique_everseen(v)) { - cout << i << ' '; + cout << i << ' '; } ``` @@ -341,29 +345,30 @@ This **does not** work with the pipe syntax. ```c++ vector v { /* ... */ }; for (auto&& w : unique_everseen(v, WidgetHash{}, WidgetEq{})) { - cout << w.name() << ' '; + cout << w.name() << ' '; } ``` unique\_justseen --------------- +---------------- Another filter adaptor that only omits consecutive duplicates. Prints `1 2 3 4 3 2 1` -Example Usage: + ```c++ vector v {1,1,1,2,2,3,3,3,4,3,2,1,1,1}; for (auto&& i : unique_justseen(v)) { - cout << i << ' '; + cout << i << ' '; } ``` If elements cannot be directly compared with equality, you can pass in a key callable. + ```c++ vector v { /* ... */ }; -for (auto&& p : unique_justseen(v, [] (const Person& p) { return p.name; })) - cout << p.name() << ' ' << p.age() << '\n'; +for (auto&& p : unique_justseen(v, [] (const Person& p) { return p.name; })) { + cout << p.name() << ' ' << p.age() << '\n'; } ``` @@ -372,11 +377,12 @@ takewhile Yields elements from an iterable until the first element that is false under the predicate is encountered. -Prints `1 2 3 4`. (5 is false under the predicate) +Prints `1 2 3 4`. (5 is false under the predicate) + ```c++ vector ivec{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1}; for (auto&& i : takewhile([] (int i) {return i < 5;}, ivec)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -386,10 +392,11 @@ Yields all elements after and including the first element that is true under the predicate. Prints `5 6 7 1 2` + ```c++ vector ivec{1, 2, 3, 4, 5, 6, 7, 1, 2}; for (auto&& i : dropwhile([] (int i) {return i < 5;}, ivec)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -397,18 +404,18 @@ cycle ----- *Additional Requirements*: Input must have a ForwardIterator - -Repeatedly produces all values of an iterable. The loop will be infinite, so a +Repeatedly produces all values of an iterable. The loop will be infinite, so a `break` or other control flow structure is necessary to exit. Prints `1 2 3` repeatedly until `some_condition` is true + ```c++ vector vec{1, 2, 3}; for (auto&& i : cycle(vec)) { - cout << i << '\n'; - if (some_condition) { - break; - } + cout << i << '\n'; + if (some_condition) { + break; + } } ``` @@ -416,19 +423,21 @@ repeat ------ Repeatedly produces a single argument forever, or a given number of times. `repeat` will bind a reference when passed an lvalue and move when given -an rvalue. It will then yield a reference to the same item until completion. +an rvalue. It will then yield a reference to the same item until completion. The below prints `1` five times. + ```c++ for (auto&& e : repeat(1, 5)) { - cout << e << '\n'; + cout << e << '\n'; } ``` The below prints `2` forever + ```c++ for (auto&& e : repeat(2)) { - cout << e << '\n'; + cout << e << '\n'; } ``` @@ -448,9 +457,10 @@ being the `std::numeric_limits::max()` for the integral type (`long` by default) The below will print `0 1 2` ... etc + ```c++ for (auto&& i : count()) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -460,31 +470,32 @@ groupby a reference, the reference must remain valid after the iterator is incremented. Roughly equivalent to requiring the Input have a ForwardIterator. -Separate an iterable into groups sharing a common key. The following example +Separate an iterable into groups sharing a common key. The following example creates a new group whenever a string of a different length is encountered. + ```c++ vector vec = { - "hi", "ab", "ho", - "abc", "def", - "abcde", "efghi" + "hi", "ab", "ho", + "abc", "def", + "abcde", "efghi" }; for (auto&& gb : groupby(vec, [] (const string &s) {return s.length(); })) { - cout << "key: " << gb.first << '\n'; - cout << "content: "; - for (auto&& s : gb.second) { - cout << s << " "; - } - cout << '\n'; + cout << "key: " << gb.first << '\n'; + cout << "content: "; + for (auto&& s : gb.second) { + cout << s << " "; + } + cout << '\n'; } ``` + *Note*: Just like Python's `itertools.groupby`, this doesn't do any sorting. It just iterates through, making a new group each time there is a key change. Thus, if the group is unsorted, the same key may appear multiple times. starmap ------- - Takes a sequence of tuple-like objects (anything that works with `std::get`) and unpacks each object into individual arguments for each function call. The below example takes a `vector` of `pairs` of ints, and passes them @@ -494,21 +505,21 @@ the first and second arguments to the function. ```c++ vector> v = {{2, 3}, {5, 2}, {3, 4}}; // {base, exponent} for (auto&& i : starmap([](int b, int e){return pow(b, e);}, v)) { - // ... + // ... } ``` `starmap` can also work over a tuple-like object of tuple-like objects even when the contained objects are different as long as the functor works with -multiple types of calls. For example, a `Callable` struct with overloads +multiple types of calls. For example, a `Callable` struct with overloads for its `operator()` will work as long as all overloads have the same return type ```c++ struct Callable { - int operator()(int i) const; - int operator()(int i, char c) const; - int operator()(double d, int i, char c) const; + int operator()(int i) const; + int operator()(int i, char c) const; + int operator()(double d, int i, char c) const; }; ``` @@ -516,36 +527,40 @@ This will work with a tuple of mixed types ```c++ auto t = make_tuple( - make_tuple(5), // first form - make_pair(3, 'c'), // second - make_tuple(1.0, 1, '1')); // third + make_tuple(5), // first form + make_pair(3, 'c'), // second + make_tuple(1.0, 1, '1')); // third for (auto&& i : starmap(Callable{}, t)) { - // ... + // ... } ``` accumulate -------- +---------- *Additional Requirements*: Type return from functor (with reference removed) must be assignable. Differs from `std::accumulate` (which in my humble opinion should be named -`std::reduce` or `std::foldl`). It is similar to a functional reduce where one -can see all of the intermediate results. By default, it keeps a running sum. +`std::reduce` or `std::foldl`). It is similar to a functional reduce where one +can see all of the intermediate results. By default, it keeps a running sum. + Prints: `1 3 6 10 15` + ```c++ for (auto&& i : accumulate(range(1, 6))) { - cout << i << '\n'; + cout << i << '\n'; } ``` + A second, optional argument may provide an alternative binary function -to compute results. The following example multiplies the numbers, rather +to compute results. The following example multiplies the numbers, rather than adding them. + Prints: `1 2 6 24 120` ```c++ for (auto&& i : accumulate(range(1, 6), std::multiplies{})) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -556,10 +571,11 @@ zip --- Takes an arbitrary number of ranges of different types and efficiently iterates over them in parallel (so an iterator to each container is incremented -simultaneously). When you dereference an iterator to "zipped" range you get a +simultaneously). When you dereference an iterator to "zipped" range you get a tuple of the elements the iterators were holding. Example usage: + ```c++ array iseq{{1,2,3,4}}; vector fseq{1.2,1.4,12.3,4.5,9.9}; @@ -567,16 +583,16 @@ vector sseq{"i","like","apples","a lot","dude"}; array dseq{{1.2,1.2,1.2,1.2,1.2}}; for (auto&& [i, f, s, d] : zip(iseq, fseq, sseq, dseq)) { - cout << i << ' ' << f << ' ' << s << ' ' << d << '\n'; - f = 2.2f; // modifies the underlying 'fseq' sequence + cout << i << ' ' << f << ' ' << s << ' ' << d << '\n'; + f = 2.2f; // modifies the underlying 'fseq' sequence } ``` zip\_longest ------------ +------------ Terminates on the longest sequence instead of the shortest. Repeatedly yields a tuple of `boost::optional`s where `T` is the type -yielded by the sequences' respective iterators. Because of its boost +yielded by the sequences' respective iterators. Because of its boost dependency, `zip_longest` is not in `itertools.hpp` and must be included separately. The following loop prints either "Just \" or "Nothing" for each @@ -586,23 +602,24 @@ element in each tuple yielded. vector v1 = {0, 1, 2, 3}; vector v2 = {10, 11}; for (auto&& [x, y] : zip_longest(v1, v2)) { - cout << '{'; - if (x) { - cout << "Just " << *x; - } else { - cout << "Nothing"; - } - cout << ", "; - if (y) { - cout << "Just " << *y; - } else { - cout << "Nothing"; - } - cout << "}\n"; + cout << '{'; + if (x) { + cout << "Just " << *x; + } else { + cout << "Nothing"; + } + cout << ", "; + if (y) { + cout << "Just " << *y; + } else { + cout << "Nothing"; + } + cout << "}\n"; } ``` The output is: + ``` {Just 0, Just 10} {Just 1, Just 11} @@ -612,27 +629,27 @@ The output is: imap ---- - -Takes a function and one or more iterables. The number of iterables must -match the number of arguments to the function. Applies the function to -each element (or elements) in the iterable(s). Terminates on the shortest +Takes a function and one or more iterables. The number of iterables must +match the number of arguments to the function. Applies the function to +each element (or elements) in the iterable(s). Terminates on the shortest sequence. Prints the squares of the numbers in vec: `1 4 9 16 25` ```c++ vector vec{1, 2, 3, 4, 5}; for (auto&& i : imap([] (int x) {return x * x;}, vec)) { - cout << i << '\n'; + cout << i << '\n'; } ``` With more than one sequence, the below adds corresponding elements from each vector together, printing `11 23 35 47 59 71` + ```c++ vector vec1{1, 3, 5, 7, 9, 11}; vector vec2{10, 20, 30, 40, 50, 60}; for (auto&& i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -640,10 +657,8 @@ for (auto&& i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) { `std::map`, and because it is more related to `itertools.imap` than the python builtin `map`. - compress -------- - Yields only the values corresponding to true in the selectors iterable. Terminates on the shortest sequence. @@ -652,7 +667,7 @@ Prints `2 6` vector ivec{1, 2, 3, 4, 5, 6}; vector bvec{false, true, false, false, false, true}; for (auto&& i : compress(ivec, bvec) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -662,10 +677,10 @@ sorted Allows iteration over a sequence in sorted order. `sorted` does **not** produce a new sequence, copy elements, or modify the original -sequence. It only provides a way to iterate over existing elements. +sequence. It only provides a way to iterate over existing elements. `sorted` also takes an optional second [comparator](http://en.cppreference.com/w/cpp/concept/Compare) -argument. If not provided, defaults to `std::less`.
+argument. If not provided, defaults to `std::less`.
Iterables passed to sorted are required to have an iterator with an `operator*() const` member. @@ -674,7 +689,7 @@ The below outputs `0 1 2 3 4`. ```c++ unordered_set nums{4, 0, 2, 1, 3}; for (auto&& i : sorted(nums)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -692,31 +707,31 @@ vector vec1{1,2,3,4,5,6}; array arr1{{7,8,9,10}}; for (auto&& i : chain(empty,vec1,arr1)) { - cout << i << '\n'; + cout << i << '\n'; } ``` chain.from\_iterable -------------------- - +-------------------- Similar to chain, but rather than taking a variadic number of iterables, it takes an iterable of iterables and chains the contained iterables together. A simple example is shown below using a vector of vectors to represent a 2d ragged array, and prints it in row-major order. + ```c++ vector> matrix = { - {1, 2, 3}, - {4, 5}, - {6, 8, 9, 10, 11, 12} + {1, 2, 3}, + {4, 5}, + {6, 8, 9, 10, 11, 12} }; for (auto&& i : chain.from_iterable(matrix)) { - cout << i << '\n'; + cout << i << '\n'; } ``` reversed -------- +-------- *Additional Requirements*: Input must be compatible with `std::rbegin()` and `std::rend()` @@ -724,7 +739,7 @@ Iterates over elements of a sequence in reverse order. ```c++ for (auto&& i : reversed(a)) { - cout << i << '\n'; + cout << i << '\n'; } ``` @@ -735,18 +750,19 @@ Returns selected elements from a range, parameters are start, stop and step. the range returned is [start,stop) where you only take every step element This outputs `0 3 6 9 12` + ```c++ vector a{0,1,2,3,4,5,6,7,8,9,10,11,12,13}; for (auto&& i : slice(a,0,15,3)) { - cout << i << '\n'; + cout << i << '\n'; } ``` sliding\_window -------------- +--------------- *Additional Requirements*: Input must have a ForwardIterator -Takes a section from a range and increments the whole section. If the +Takes a section from a range and increments the whole section. If the window size is larger than the length of the input, the `sliding_window` will yield nothing (begin == end). @@ -764,60 +780,65 @@ take a section of size 4, output is: ``` Example Usage: + ```c++ vector v = {1,2,3,4,5,6,7,8,9}; for (auto&& sec : sliding_window(v,4)) { - for (auto&& i : sec) { - cout << i << ' '; - i.get() = 90; - } - cout << '\n'; + for (auto&& i : sec) { + cout << i << ' '; + i.get() = 90; + } + cout << '\n'; } ``` -chunked ------- +chunked +------- chunked will yield subsequent chunks of an iterable in blocks of a specified size. The final chunk may be shorter than the rest if the chunk size given does not evenly divide the length of the iterable. Example usage: + ```c++ vector v {1,2,3,4,5,6,7,8,9}; for (auto&& sec : chunked(v,4)) { - for (auto&& i : sec) { - cout << i << ' '; - } - cout << '\n'; + for (auto&& i : sec) { + cout << i << ' '; + } + cout << '\n'; } ``` The above prints: + ``` 1 2 3 4 5 6 7 8 9 ``` + batched ------- - batched will yield a given number N of batches containing subsequent elements from an iterable, assuming the iterable contains at least N elements. The size of each batch is immaterial, but the implementation guarantees that no two batches will differ in size by more than 1. Example usage: + ```c++ vector v {1,2,3,4,5,6,7,8,9}; for (auto&& sec : batched(v,4)) { - for (auto&& i : sec) { - cout << i << ' '; - } - cout << '\n'; + for (auto&& i : sec) { + cout << i << ' '; + } + cout << '\n'; } ``` The above prints: + ``` 1 2 3 4 5 @@ -826,54 +847,60 @@ The above prints: ``` product ------- +------- *Additional Requirements*: Input must have a ForwardIterator Generates the cartesian product of the given ranges put together. Example usage: + ```c++ vector v1{1,2,3}; vector v2{7,8}; vector v3{"the","cat"}; vector v4{"hi","what's","up","dude"}; for (auto&& [a, b, c, d] : product(v1,v2,v3,v4)) { - cout << a << ", " << b << ", " << c << ", " << d << '\n'; + cout << a << ", " << b << ", " << c << ", " << d << '\n'; } ``` -Product also accepts a "repeat" as a template argument. Currently this is the only way to do repeats. **If you are reading this and need `product(seq, 3)` instead of `product<3>(seq)` please open an issue**. +Product also accepts a "repeat" as a template argument. Currently this is the +only way to do repeats. **If you are reading this and need `product(seq, 3)` +instead of `product<3>(seq)` please open an issue**. Example usage: + ```c++ std::string s = "abc"; // equivalent of product(s, s, s); for (auto&& t : product<3>(s)) { - // ... + // ... } ``` combinations ------------ +------------ *Additional Requirements*: Input must have a ForwardIterator Generates n length unique sequences of the input range. Example usage: + ```c++ vector v = {1,2,3,4,5}; for (auto&& i : combinations(v,3)) { - for (auto&& j : i ) cout << j << " "; - cout << '\n'; + for (auto&& j : i ) cout << j << " "; + cout << '\n'; } ``` combinations\_with\_replacement ------------------------------ +------------------------------- *Additional Requirements*: Input must have a ForwardIterator -Like combinations, but with replacement of each element. The +Like combinations, but with replacement of each element. The below is printed by the loop that follows: + ``` {A, A} {A, B} @@ -882,43 +909,46 @@ below is printed by the loop that follows: {B, C} {C, C} ``` + ```c++ for (auto&& v : combinations_with_replacement(s, 2)) { - cout << '{' << v[0] << ", " << v[1] << "}\n"; + cout << '{' << v[0] << ", " << v[1] << "}\n"; } ``` permutations ------------ -*Additional Requirements*: Input must have a ForwardIterator. Iterator must +------------ +*Additional Requirements*: Input must have a ForwardIterator. Iterator must have an `operator*() const`. Generates all the permutations of a range using `std::next_permutation`. Example usage: + ```c++ vector v = {1,2,3,4,5}; for (auto&& vec : permutations(v)) { - for (auto&& i : vec) { - cout << i << ' '; - } - cout << '\n'; + for (auto&& i : vec) { + cout << i << ' '; + } + cout << '\n'; } ``` powerset -------- +-------- *Additional Requirements*: Input must have a ForwardIterator Generates every possible subset of a set, runs in O(2^n). Example usage: + ```c++ vector vec {1,2,3,4,5,6,7,8,9}; for (auto&& v : powerset(vec)) { - for (auto&& i : v) { - cout << i << " "; - } - cout << '\n'; + for (auto&& i : v) { + cout << i << " "; + } + cout << '\n'; } ```