diff --git a/include/kf/stl/backport/ranges/detail/adjacent_view.h b/include/kf/stl/backport/ranges/detail/adjacent_view.h new file mode 100644 index 0000000..072c339 --- /dev/null +++ b/include/kf/stl/backport/ranges/detail/adjacent_view.h @@ -0,0 +1,135 @@ +#pragma once +#include +#include + +namespace kf +{ + namespace ranges + { + template requires std::ranges::view && (N > 0) && std::ranges::common_range + class adjacent_view : public std::ranges::view_interface> + { + public: + adjacent_view() requires std::default_initializable = default; + + explicit adjacent_view(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{ std::ranges::end(m_base) }; + } + + private: + class iterator + { + 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) + { + m_current.front() = first; + for (size_t i = 1; i < N; ++i) + { + std::ranges::advance(first, 1, last); + m_current[i] = first; + } + } + + 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 + { + return transformToTuple(m_current); + } + + constexpr iterator& operator++() + { + for (auto& iter : m_current) + { + ++iter; + } + return *this; + } + + constexpr bool operator==(const iterator& other) const + { + return m_current.back() == other.m_current.back(); + } + + private: + 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])...); + } + }; + + 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)); + } + + // Enables `range | views::adjacent` + template requires std::ranges::forward_range + friend auto operator|(R&& r, const adjacent_fn& adj) + { + return adj(std::forward(r)); + } + }; + } + + namespace views + { + template + constexpr kf::ranges::adjacent_fn adjacent; + + inline constexpr kf::ranges::adjacent_fn<2> pairwise; + } +} diff --git a/include/kf/stl/backport/ranges/ranges b/include/kf/stl/backport/ranges/ranges new file mode 100644 index 0000000..fbe4269 --- /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 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