Skip to content

Commit daabf57

Browse files
Implement MyChunkView for libc++ (#305)
1 parent 8da9212 commit daabf57

4 files changed

Lines changed: 170 additions & 2 deletions

File tree

solvers/cpp/src/bin/y2022_d03.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "answer.hpp"
2+
#include "my_chunk_view.hpp"
23
#include "my_fold_left_first.hpp"
34
#include "solver.hpp"
45
#include "utils.hpp"
@@ -163,7 +164,7 @@ constexpr auto solve2(auto inputStr) {
163164
constexpr std::size_t groupSize{3};
164165

165166
return sumScore(
166-
splitLinesUntilEmpty(inputStr) | views::chunk(groupSize) |
167+
splitLinesUntilEmpty(inputStr) | MyChunk(groupSize) |
167168
views::transform([](auto&& group) { return getGroupBadgeItem(group); }));
168169
}
169170

solvers/cpp/src/lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
add_library(lib INTERFACE)
22
target_include_directories(lib INTERFACE ${CMAKE_CURRENT_LIST_DIR})
33

4-
add_executable(lib-tests my_enumerate_view.test.cpp my_fold_left_first.test.cpp my_slide_view.test.cpp my_stride_view.test.cpp)
4+
add_executable(lib-tests my_chunk_view.test.cpp my_enumerate_view.test.cpp my_fold_left_first.test.cpp my_slide_view.test.cpp my_stride_view.test.cpp)
55
target_link_libraries(lib-tests PRIVATE test_utils GTest::gtest_main)
66
gtest_discover_tests(lib-tests)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#pragma once
2+
3+
#include <ranges>
4+
5+
#if !defined(TEST_MY_CHUNK_VIEW) && __cpp_lib_ranges_chunk >= 202202L
6+
7+
inline constexpr auto MyChunk = std::views::chunk;
8+
9+
#else
10+
11+
namespace internal {
12+
13+
// Only supported via MyChunk adaptor
14+
15+
template <std::ranges::viewable_range Rng>
16+
class MyChunkView : public std::ranges::view_interface<MyChunkView<Rng>> {
17+
using Base = Rng;
18+
19+
class Iterator {
20+
using BaseIter = std::ranges::iterator_t<Base>;
21+
using BaseSentinel = std::ranges::sentinel_t<Base>;
22+
23+
public:
24+
using difference_type = std::ranges::range_difference_t<Base>;
25+
using value_type = decltype(std::ranges::subrange<BaseIter>());
26+
27+
explicit Iterator(BaseIter begin, BaseSentinel end, std::size_t n) noexcept(
28+
noexcept(std::ranges::advance(_curChunkEnd, _n, _end)))
29+
: _curChunkBegin{std::move(begin)},
30+
_curChunkEnd{_curChunkBegin},
31+
_end{std::move(end)},
32+
_n{n} {
33+
std::ranges::advance(_curChunkEnd, _n, _end);
34+
}
35+
36+
constexpr auto operator*() const
37+
noexcept(noexcept(std::ranges::subrange(_curChunkBegin, _curChunkEnd))) {
38+
return std::ranges::subrange(_curChunkBegin, _curChunkEnd);
39+
}
40+
41+
constexpr Iterator& operator++() noexcept(
42+
noexcept(std::ranges::advance(_curChunkBegin, _n, _end)) &&
43+
noexcept(std::ranges::advance(_curChunkEnd, _n, _end))) {
44+
std::ranges::advance(_curChunkBegin, _n, _end);
45+
std::ranges::advance(_curChunkEnd, _n, _end);
46+
return *this;
47+
}
48+
constexpr void operator++(int) noexcept(noexcept(++(*this))) { ++(*this); }
49+
50+
constexpr bool operator==(const Iterator& o) const
51+
noexcept(noexcept(_curChunkBegin == o._curChunkBegin)) {
52+
return _curChunkBegin == o._curChunkBegin;
53+
}
54+
constexpr bool operator==(std::default_sentinel_t /*unused*/) const
55+
noexcept(noexcept(_curChunkBegin == _end)) {
56+
return _curChunkBegin == _end;
57+
}
58+
59+
private:
60+
BaseIter _curChunkBegin;
61+
BaseIter _curChunkEnd;
62+
BaseSentinel _end;
63+
std::size_t _n;
64+
};
65+
66+
public:
67+
explicit MyChunkView(Rng&& rng, std::size_t n) noexcept(
68+
noexcept(Base(std::views::all(std::move(rng)))))
69+
: _base{std::views::all(std::move(rng))}, _n{n} {}
70+
71+
[[nodiscard]] constexpr auto begin() const
72+
noexcept(noexcept(Iterator(std::ranges::begin(_base),
73+
std::ranges::end(_base), _n)))
74+
requires std::ranges::range<const Base>
75+
{
76+
return Iterator{std::ranges::begin(_base), std::ranges::end(_base), _n};
77+
}
78+
[[nodiscard]] constexpr auto begin() noexcept(noexcept(
79+
Iterator(std::ranges::begin(_base), std::ranges::end(_base), _n))) {
80+
return Iterator{std::ranges::begin(_base), std::ranges::end(_base), _n};
81+
}
82+
83+
static constexpr auto end() noexcept { return std::default_sentinel; }
84+
85+
private:
86+
Base _base;
87+
std::size_t _n;
88+
};
89+
90+
class MyChunkAdaptorClosure
91+
: public std::ranges::range_adaptor_closure<MyChunkAdaptorClosure> {
92+
public:
93+
constexpr explicit MyChunkAdaptorClosure(std::size_t n) noexcept : _n{n} {}
94+
95+
template <std::ranges::viewable_range Rng>
96+
constexpr auto operator()(Rng&& rng) const noexcept(
97+
noexcept(MyChunkView<decltype(std::views::all(std::forward<Rng>(rng)))>(
98+
std::views::all(std::forward<Rng>(rng)), _n))) {
99+
auto rngAll = std::views::all(std::forward<Rng>(rng));
100+
return MyChunkView<decltype(rngAll)>{std::move(rngAll), _n};
101+
}
102+
103+
private:
104+
std::size_t _n;
105+
};
106+
107+
} // namespace internal
108+
109+
class MyChunkAdaptor {
110+
public:
111+
constexpr internal::MyChunkAdaptorClosure operator()(
112+
std::size_t n) const noexcept {
113+
return internal::MyChunkAdaptorClosure{n};
114+
}
115+
};
116+
117+
inline constexpr MyChunkAdaptor MyChunk{};
118+
119+
#endif
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#define TEST_MY_CHUNK_VIEW
2+
#include "my_chunk_view.hpp"
3+
4+
#include <gtest/gtest.h>
5+
6+
#include <ranges>
7+
#include <span>
8+
#include <string>
9+
#include <tuple>
10+
11+
#if defined(_LIBCPP_VERSION) && __cpp_lib_ranges_chunk >= 202202L
12+
#error "libc++ started supporting std::views::chunk. Use it directly"
13+
#endif
14+
15+
namespace ranges = std::ranges;
16+
namespace views = std::views;
17+
18+
using Chunked = std::vector<std::string>;
19+
20+
TEST(MyChunkView, BetweenPipes) {
21+
const std::string data{"123456789"};
22+
auto processed =
23+
data | views::drop(1) | MyChunk(3) | views::take(2) | ranges::to<Chunked>();
24+
ASSERT_EQ(processed.size(), 2);
25+
EXPECT_EQ(processed[0], "234");
26+
EXPECT_EQ(processed[1], "567");
27+
}
28+
29+
TEST(MyChunkView, EmptyData) {
30+
const std::string empty;
31+
auto processed = empty | MyChunk(2) | ranges::to<std::vector>();
32+
EXPECT_TRUE(processed.empty());
33+
}
34+
35+
TEST(MyChunkView, EqualSize) {
36+
const std::string one{"123"};
37+
auto processed = one | MyChunk(3) | ranges::to<Chunked>();
38+
ASSERT_EQ(processed.size(), 1);
39+
EXPECT_EQ(processed[0], "123");
40+
}
41+
42+
TEST(MyChunkView, LargerThanInput) {
43+
const std::string data{"12345"};
44+
auto processed = data | MyChunk(4) | ranges::to<Chunked>();
45+
ASSERT_EQ(processed.size(), 2);
46+
EXPECT_EQ(processed[0], "1234");
47+
EXPECT_EQ(processed[1], "5");
48+
}

0 commit comments

Comments
 (0)