Skip to content

Commit 8da9212

Browse files
Implement MyEnumerateView for libc++ (#304)
1 parent fcf3b33 commit 8da9212

4 files changed

Lines changed: 167 additions & 2 deletions

File tree

solvers/cpp/src/bin/y2024_d07.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "answer.hpp"
22
#include "convert.hpp"
3+
#include "my_enumerate_view.hpp"
34
#include "my_fold_left_first.hpp"
45
#include "solver.hpp"
56
#include "utils.hpp"
@@ -130,7 +131,7 @@ class PossibleEquation {
130131
template <bool allowConcatenationOperator>
131132
auto solve(std::string_view input) {
132133
auto result = MyFoldLeftFirst(
133-
splitLinesUntilEmpty(input) | views::enumerate |
134+
splitLinesUntilEmpty(input) | MyEnumerate |
134135
views::transform([](auto&& args) -> std::optional<uint64_t> {
135136
auto [index, line] = args;
136137
auto parts = line | views::split(':') | ranges::to<std::vector>();

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_fold_left_first.test.cpp my_slide_view.test.cpp my_stride_view.test.cpp)
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)
55
target_link_libraries(lib-tests PRIVATE test_utils GTest::gtest_main)
66
gtest_discover_tests(lib-tests)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#pragma once
2+
3+
#include <ranges>
4+
5+
#if !defined(TEST_MY_ENUMERATE_VIEW) && __cpp_lib_ranges_enumerate >= 202302L
6+
7+
inline constexpr auto MyEnumerate = std::views::enumerate;
8+
9+
#else
10+
11+
namespace internal {
12+
13+
// Only supported via MyEnumerate adaptor
14+
15+
template <std::ranges::viewable_range Rng>
16+
class MyEnumerateView
17+
: public std::ranges::view_interface<MyEnumerateView<Rng>> {
18+
using Base = Rng;
19+
20+
class Iterator {
21+
using BaseIter = std::ranges::iterator_t<Base>;
22+
using BaseSentinel = std::ranges::sentinel_t<Base>;
23+
24+
public:
25+
using difference_type = std::ranges::range_difference_t<Base>;
26+
using value_type =
27+
std::tuple<difference_type, std::ranges::range_value_t<Base>>;
28+
29+
explicit Iterator(BaseIter begin, BaseSentinel end) noexcept
30+
: _cur{std::move(begin)}, _end{std::move(end)} {}
31+
32+
constexpr auto operator*() const noexcept(noexcept(
33+
std::tuple<difference_type, std::ranges::range_reference_t<Base>>(
34+
_count, *_cur))) {
35+
return std::tuple<difference_type, std::ranges::range_reference_t<Base>>(
36+
_count, *_cur);
37+
}
38+
39+
constexpr Iterator& operator++() noexcept(
40+
noexcept(std::ranges::advance(_cur, 1)) && noexcept(++_count)) {
41+
std::ranges::advance(_cur, 1);
42+
++_count;
43+
return *this;
44+
}
45+
constexpr void operator++(int) noexcept(noexcept(++(*this))) { ++(*this); }
46+
47+
constexpr bool operator==(const Iterator& o) const
48+
noexcept(noexcept(_cur == o._cur)) {
49+
return _cur == o._cur;
50+
}
51+
constexpr bool operator==(std::default_sentinel_t /*unused*/) const
52+
noexcept(noexcept(_cur == _end)) {
53+
return _cur == _end;
54+
}
55+
56+
private:
57+
BaseIter _cur;
58+
BaseSentinel _end;
59+
difference_type _count{0};
60+
};
61+
62+
public:
63+
explicit MyEnumerateView(Rng&& rng) noexcept(
64+
noexcept(Base(std::views::all(std::move(rng)))))
65+
: _base{std::views::all(std::move(rng))} {}
66+
67+
[[nodiscard]] constexpr auto begin() const
68+
noexcept(noexcept(Iterator(std::ranges::begin(_base),
69+
std::ranges::end(_base))))
70+
requires std::ranges::range<const Base>
71+
{
72+
return Iterator{std::ranges::begin(_base), std::ranges::end(_base)};
73+
}
74+
[[nodiscard]] constexpr auto begin() noexcept(
75+
noexcept(Iterator(std::ranges::begin(_base), std::ranges::end(_base)))) {
76+
return Iterator{std::ranges::begin(_base), std::ranges::end(_base)};
77+
}
78+
79+
static constexpr auto end() noexcept { return std::default_sentinel; }
80+
81+
private:
82+
Base _base;
83+
};
84+
85+
class MyEnumerateAdaptorClosure
86+
: public std::ranges::range_adaptor_closure<MyEnumerateAdaptorClosure> {
87+
public:
88+
template <std::ranges::viewable_range Rng>
89+
constexpr auto operator()(Rng&& rng) const noexcept(
90+
noexcept(MyEnumerateView<decltype(std::views::all(std::forward<Rng>(rng)))>(
91+
std::views::all(std::forward<Rng>(rng))))) {
92+
auto rngAll = std::views::all(std::forward<Rng>(rng));
93+
return MyEnumerateView<decltype(rngAll)>{std::move(rngAll)};
94+
}
95+
};
96+
97+
} // namespace internal
98+
99+
inline constexpr internal::MyEnumerateAdaptorClosure MyEnumerate{};
100+
101+
#endif
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#define TEST_MY_ENUMERATE_VIEW
2+
#include "my_enumerate_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_enumerate >= 202302L
12+
#error "libc++ started supporting std::views::enumerate. Use it directly"
13+
#endif
14+
15+
namespace ranges = std::ranges;
16+
namespace views = std::views;
17+
18+
using EnumeratedChar = std::tuple<size_t, char>;
19+
using Enumerated = std::vector<EnumeratedChar>;
20+
21+
namespace {
22+
23+
template <typename T>
24+
auto makeEnumeratedChar(size_t i, T&& v) {
25+
return std::make_tuple<size_t, T>(std::move(i), std::forward<T>(v));
26+
}
27+
28+
} // namespace
29+
30+
TEST(MyEnumerateView, BetweenPipes) {
31+
const std::string data{"123456789"};
32+
auto processed = data | views::drop(1) | MyEnumerate | views::take(4) |
33+
ranges::to<Enumerated>();
34+
ASSERT_EQ(processed.size(), 4);
35+
EXPECT_EQ(processed[0], makeEnumeratedChar(0, '2'));
36+
EXPECT_EQ(processed[1], makeEnumeratedChar(1, '3'));
37+
EXPECT_EQ(processed[2], makeEnumeratedChar(2, '4'));
38+
EXPECT_EQ(processed[3], makeEnumeratedChar(3, '5'));
39+
}
40+
41+
TEST(MyEnumerateView, NoElements) {
42+
const std::string empty;
43+
auto processed = empty | MyEnumerate | ranges::to<Enumerated>();
44+
EXPECT_TRUE(processed.empty());
45+
}
46+
47+
TEST(MyEnumerateView, SingleElement) {
48+
const std::string one{"1"};
49+
auto processed = one | MyEnumerate | ranges::to<Enumerated>();
50+
ASSERT_EQ(processed.size(), 1);
51+
EXPECT_EQ(processed[0], makeEnumeratedChar(0, '1'));
52+
}
53+
54+
TEST(MyEnumerateView, ManyElements) {
55+
const std::string data{"12345"};
56+
auto processed = data | MyEnumerate | ranges::to<Enumerated>();
57+
ASSERT_EQ(processed.size(), 5);
58+
EXPECT_EQ(processed[0], makeEnumeratedChar(0, '1'));
59+
EXPECT_EQ(processed[1], makeEnumeratedChar(1, '2'));
60+
EXPECT_EQ(processed[2], makeEnumeratedChar(2, '3'));
61+
EXPECT_EQ(processed[3], makeEnumeratedChar(3, '4'));
62+
EXPECT_EQ(processed[4], makeEnumeratedChar(4, '5'));
63+
}

0 commit comments

Comments
 (0)