From 0fade9aea4c034bcc02db0d161777861650e52f4 Mon Sep 17 00:00:00 2001 From: Serhii Malyi Date: Tue, 10 Jun 2025 16:10:57 +0300 Subject: [PATCH 1/3] Implement adjacent view functionality --- include/kf/AdjacentView.h | 117 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 include/kf/AdjacentView.h diff --git a/include/kf/AdjacentView.h b/include/kf/AdjacentView.h new file mode 100644 index 0000000..7e08d83 --- /dev/null +++ b/include/kf/AdjacentView.h @@ -0,0 +1,117 @@ +#pragma once +#include +#include + +namespace kf +{ + template + requires std::ranges::view && (N > 0) && std::ranges::common_range + class AdjacentView : public std::ranges::view_interface> + { + public: + AdjacentView() requires std::default_initializable = default; + + explicit AdjacentView(Range base) + : m_base(std::move(base)) + { + } + + constexpr auto begin() + { + return Iterator{ std::ranges::begin(m_base), std::ranges::end(m_base) }; + } + + constexpr auto end() + { + return Iterator{ AsSentinel{}, std::ranges::end(m_base) }; + } + + private: + struct AsSentinel + { + explicit AsSentinel() = default; + }; + + struct Iterator + { + using BaseIterator = std::ranges::iterator_t; + std::array m_current{}; + + constexpr Iterator(BaseIterator first, std::ranges::sentinel_t last) + { + m_current.front() = first; + for (size_t i = 1; i < N; ++i) + { + std::ranges::advance(first, 1, last); + m_current[i] = first; + } + } + + constexpr Iterator(AsSentinel, BaseIterator last) + { + if constexpr (!std::ranges::bidirectional_range) + { + m_current.fill(last); + } + else + { + m_current.back() = last; + for (size_t i = 1; i < N; ++i) + { + std::ranges::advance(last, -1, last); + m_current[N - 1 - i] = last; + } + } + } + + template + constexpr auto arrayToTuple(const decltype(m_current)& arr, std::index_sequence) const + { + return std::tie((*arr[I])...); + } + + constexpr auto transformToTuple(const decltype(m_current)& arr) const + { + return arrayToTuple(arr, std::make_index_sequence{}); + } + + constexpr auto operator*() const + { + return transformToTuple(m_current); + } + + constexpr Iterator& operator++() + { + for (BaseIterator& iter : m_current) + { + ++iter; + } + return *this; + } + + constexpr bool operator==(const Iterator& other) const + { + return m_current.back() == other.m_current.back(); + } + }; + + private: + Range m_base{}; + }; + + template + class AdjacentFn + { + public: + template + requires std::ranges::viewable_range + auto operator()(R&& r) const + { + return AdjacentView, N>(std::forward(r)); + } + }; + + template + constexpr AdjacentFn adjacent; + inline constexpr AdjacentFn<2> pairwise; +} From d4a37e2ebc306e4883c5b5f39119d211f34c6249 Mon Sep 17 00:00:00 2001 From: Serhii Malyi Date: Wed, 11 Jun 2025 13:02:32 +0300 Subject: [PATCH 2/3] Move adjacent view header to mimic STL files structure --- .../backport/ranges/detail/adjacent_view.h} | 36 +++++++++---------- include/kf/stl/backport/ranges/ranges | 2 ++ 2 files changed, 19 insertions(+), 19 deletions(-) rename include/kf/{AdjacentView.h => stl/backport/ranges/detail/adjacent_view.h} (92%) create mode 100644 include/kf/stl/backport/ranges/ranges diff --git a/include/kf/AdjacentView.h b/include/kf/stl/backport/ranges/detail/adjacent_view.h similarity index 92% rename from include/kf/AdjacentView.h rename to include/kf/stl/backport/ranges/detail/adjacent_view.h index 7e08d83..16c1f57 100644 --- a/include/kf/AdjacentView.h +++ b/include/kf/stl/backport/ranges/detail/adjacent_view.h @@ -23,20 +23,29 @@ namespace kf constexpr auto end() { - return Iterator{ AsSentinel{}, std::ranges::end(m_base) }; + return Iterator{ std::ranges::end(m_base) }; } private: - struct AsSentinel - { - explicit AsSentinel() = default; - }; - - struct Iterator + class Iterator { + private: using BaseIterator = std::ranges::iterator_t; std::array m_current{}; + private: + constexpr auto transformToTuple(const decltype(m_current)& arr) const + { + return arrayToTuple(arr, std::make_index_sequence{}); + } + + template + constexpr auto arrayToTuple(const decltype(m_current)& arr, std::index_sequence) const + { + return std::tie((*arr[I])...); + } + + public: constexpr Iterator(BaseIterator first, std::ranges::sentinel_t last) { m_current.front() = first; @@ -47,7 +56,7 @@ namespace kf } } - constexpr Iterator(AsSentinel, BaseIterator last) + constexpr Iterator(BaseIterator last) { if constexpr (!std::ranges::bidirectional_range) { @@ -64,17 +73,6 @@ namespace kf } } - template - constexpr auto arrayToTuple(const decltype(m_current)& arr, std::index_sequence) const - { - return std::tie((*arr[I])...); - } - - constexpr auto transformToTuple(const decltype(m_current)& arr) const - { - return arrayToTuple(arr, std::make_index_sequence{}); - } - constexpr auto operator*() const { return transformToTuple(m_current); diff --git a/include/kf/stl/backport/ranges/ranges b/include/kf/stl/backport/ranges/ranges new file mode 100644 index 0000000..7c2ca8a --- /dev/null +++ b/include/kf/stl/backport/ranges/ranges @@ -0,0 +1,2 @@ +#pragma once +#include "detail\adjacent_view.h" \ No newline at end of file From b20953ad7dead8da3aa19c1a35394d56abb79987 Mon Sep 17 00:00:00 2001 From: Sergey Podobry Date: Tue, 29 Jul 2025 22:37:52 +0300 Subject: [PATCH 3/3] Add some improvements and tests --- .../backport/ranges/detail/adjacent_view.h | 172 ++++++++++-------- include/kf/stl/backport/ranges/ranges | 2 +- test/AdjacentView.cpp | 34 ++++ test/CMakeLists.txt | 1 + test/pch.h | 1 + 5 files changed, 133 insertions(+), 77 deletions(-) create mode 100644 test/AdjacentView.cpp diff --git a/include/kf/stl/backport/ranges/detail/adjacent_view.h b/include/kf/stl/backport/ranges/detail/adjacent_view.h index 16c1f57..072c339 100644 --- a/include/kf/stl/backport/ranges/detail/adjacent_view.h +++ b/include/kf/stl/backport/ranges/detail/adjacent_view.h @@ -4,112 +4,132 @@ namespace kf { - template - requires std::ranges::view && (N > 0) && std::ranges::common_range - class AdjacentView : public std::ranges::view_interface> + namespace ranges { - public: - AdjacentView() requires std::default_initializable = default; - - explicit AdjacentView(Range base) - : m_base(std::move(base)) - { - } - - constexpr auto begin() + template requires std::ranges::view && (N > 0) && std::ranges::common_range + class adjacent_view : public std::ranges::view_interface> { - return Iterator{ std::ranges::begin(m_base), std::ranges::end(m_base) }; - } - - constexpr auto end() - { - return Iterator{ std::ranges::end(m_base) }; - } + public: + adjacent_view() requires std::default_initializable = default; - private: - class Iterator - { - private: - using BaseIterator = std::ranges::iterator_t; - std::array m_current{}; + explicit adjacent_view(Range base) + : m_base(std::move(base)) + { + } - private: - constexpr auto transformToTuple(const decltype(m_current)& arr) const + constexpr auto begin() { - return arrayToTuple(arr, std::make_index_sequence{}); + return iterator{ std::ranges::begin(m_base), std::ranges::end(m_base) }; } - template - constexpr auto arrayToTuple(const decltype(m_current)& arr, std::index_sequence) const + constexpr auto end() { - return std::tie((*arr[I])...); + return iterator{ std::ranges::end(m_base) }; } - public: - constexpr Iterator(BaseIterator first, std::ranges::sentinel_t last) + private: + class iterator { - m_current.front() = first; - for (size_t i = 1; i < N; ++i) + private: + using range_iterator = std::ranges::iterator_t; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = void*; // fixme + using difference_type = std::ptrdiff_t; + using pointer = void*; // fixme + using reference = void*; // fixme + + constexpr iterator(range_iterator first, std::ranges::sentinel_t last) { - std::ranges::advance(first, 1, last); - m_current[i] = first; + m_current.front() = first; + for (size_t i = 1; i < N; ++i) + { + std::ranges::advance(first, 1, last); + m_current[i] = first; + } } - } - constexpr Iterator(BaseIterator last) - { - if constexpr (!std::ranges::bidirectional_range) + constexpr iterator(range_iterator last) + { + if constexpr (!std::ranges::bidirectional_range) + { + m_current.fill(last); + } + else + { + m_current.back() = last; + for (size_t i = 1; i < N; ++i) + { + std::ranges::advance(last, -1, last); + m_current[N - 1 - i] = last; + } + } + } + + constexpr auto operator*() const { - m_current.fill(last); + return transformToTuple(m_current); } - else + + constexpr iterator& operator++() { - m_current.back() = last; - for (size_t i = 1; i < N; ++i) + for (auto& iter : m_current) { - std::ranges::advance(last, -1, last); - m_current[N - 1 - i] = last; + ++iter; } + return *this; } - } - constexpr auto operator*() const - { - return transformToTuple(m_current); - } + constexpr bool operator==(const iterator& other) const + { + return m_current.back() == other.m_current.back(); + } - constexpr Iterator& operator++() - { - for (BaseIterator& iter : m_current) + private: + std::array m_current{}; + + private: + constexpr auto transformToTuple(const decltype(m_current)& arr) const { - ++iter; + return arrayToTuple(arr, std::make_index_sequence{}); } - return *this; + + template + constexpr auto arrayToTuple(const decltype(m_current)& arr, std::index_sequence) const + { + return std::tie((*arr[I])...); + } + }; + + private: + Range m_base{}; + }; + + template + class adjacent_fn + { + public: + template requires std::ranges::forward_range + auto operator()(R&& r) const + { + return adjacent_view, N>(std::forward(r)); } - constexpr bool operator==(const Iterator& other) const + // Enables `range | views::adjacent` + template requires std::ranges::forward_range + friend auto operator|(R&& r, const adjacent_fn& adj) { - return m_current.back() == other.m_current.back(); + return adj(std::forward(r)); } }; + } - private: - Range m_base{}; - }; - - template - class AdjacentFn + namespace views { - public: - template - requires std::ranges::viewable_range - auto operator()(R&& r) const - { - return AdjacentView, N>(std::forward(r)); - } - }; + template + constexpr kf::ranges::adjacent_fn adjacent; - template - constexpr AdjacentFn adjacent; - inline constexpr AdjacentFn<2> pairwise; + inline constexpr kf::ranges::adjacent_fn<2> pairwise; + } } diff --git a/include/kf/stl/backport/ranges/ranges b/include/kf/stl/backport/ranges/ranges index 7c2ca8a..fbe4269 100644 --- a/include/kf/stl/backport/ranges/ranges +++ b/include/kf/stl/backport/ranges/ranges @@ -1,2 +1,2 @@ #pragma once -#include "detail\adjacent_view.h" \ No newline at end of file +#include "detail/adjacent_view.h" \ No newline at end of file diff --git a/test/AdjacentView.cpp b/test/AdjacentView.cpp new file mode 100644 index 0000000..023c42d --- /dev/null +++ b/test/AdjacentView.cpp @@ -0,0 +1,34 @@ +#include "pch.h" +#include + +SCENARIO("views::adjacent") +{ + GIVEN("array with 4 elements: 1,2,3,4") + { + std::array arr = { 1, 2, 3, 4 }; + + WHEN("create adjacent view for 1 elements") + { + auto view = arr | kf::views::adjacent<1>; + + THEN("view contains 4 tuples: (1)(2)(3)(4)") + { + std::array, 4> expected = { { {1}, {2}, {3}, {4} } }; + + REQUIRE(std::equal(view.begin(), view.end(), expected.begin(), expected.end())); + } + } + + WHEN("create adjacent view for 2 elements") + { + auto view = arr | kf::views::adjacent<2>; + + THEN("view contains 3 tuples: (1,2)(2,3)(3,4)") + { + std::array, 3> expected = { { {1, 2}, {2, 3}, {3, 4} } }; + + REQUIRE(std::equal(view.begin(), view.end(), expected.begin(), expected.end())); + } + } + } +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5b53301..0e047cd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,6 +38,7 @@ find_package(WDK REQUIRED) wdk_add_driver(kf-test WINVER NTDDI_WIN10 STL pch.h pch.cpp + AdjacentView.cpp HexTest.cpp Vector.cpp ) diff --git a/test/pch.h b/test/pch.h index f464b8d..b5c57a3 100644 --- a/test/pch.h +++ b/test/pch.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include