Skip to content

Commit bb5a5b3

Browse files
authored
Merge pull request #29 from acd1034/25-unreachable-trailing_view
✨ Unreachable `trailing_view`
2 parents dc37846 + e5970bc commit bb5a5b3

File tree

2 files changed

+73
-34
lines changed

2 files changed

+73
-34
lines changed

include/strtpl/trailing_view.hpp

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
/// @file trailing_view.hpp
2-
#include <iterator> // std::iterator_traits
2+
#include <concepts> // std::semiregular
3+
#include <iterator> // std::iterator_traits, std::unreachable_sentinel_t
34
#include <ranges>
45
#include <type_traits>
56
#include <utility>
67

78
namespace strtpl {
8-
template <std::ranges::input_range View>
9+
template <class T, class U>
10+
concept different_from = !std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>;
11+
12+
// clang-format off
13+
template <std::ranges::input_range View, std::semiregular Bound = std::unreachable_sentinel_t>
914
requires std::ranges::view<View>
10-
struct trailing_view : std::ranges::view_interface<trailing_view<View>> {
15+
and (std::convertible_to<Bound, std::ranges::range_difference_t<View>> or std::same_as<Bound, std::unreachable_sentinel_t>)
16+
struct trailing_view : std::ranges::view_interface<trailing_view<View, Bound>> {
17+
// clang-format on
1118
private:
1219
[[no_unique_address]] View base_ = View();
13-
std::ranges::range_difference_t<View> count_ = 0;
20+
Bound bound_ = Bound();
1421

1522
template <bool Const>
1623
class iterator;
1724

1825
public:
1926
trailing_view() requires std::default_initializable<View>
2027
= default;
21-
constexpr trailing_view(View base, std::ranges::range_difference_t<View> count)
22-
: base_(std::move(base)), count_(std::move(count)) {
23-
assert(count_ >= 0);
28+
constexpr trailing_view(View base) : base_(std::move(base)) {}
29+
constexpr trailing_view(View base, Bound bound)
30+
: base_(std::move(base)), bound_(std::move(bound)) {
31+
assert(bound_ >= 0);
2432
}
2533

2634
constexpr View
@@ -31,9 +39,9 @@ namespace strtpl {
3139
base() && {
3240
return std::move(base_);
3341
}
34-
constexpr std::ranges::range_difference_t<View>
42+
constexpr Bound
3543
count() const noexcept {
36-
return count_;
44+
return bound_;
3745
}
3846

3947
constexpr iterator<false>
@@ -50,37 +58,43 @@ namespace strtpl {
5058
return std::default_sentinel;
5159
}
5260
constexpr iterator<false>
53-
end() requires std::ranges::common_range<View> {
54-
return {*this, std::ranges::end(base_), count_};
61+
end() requires
62+
std::ranges::common_range<View> and different_from<Bound, std::unreachable_sentinel_t> {
63+
return {*this, std::ranges::end(base_), bound_};
5564
}
5665
constexpr auto
5766
end() const requires std::ranges::input_range<const View> {
5867
return std::default_sentinel;
5968
}
6069
constexpr iterator<true>
61-
end() const requires
62-
std::ranges::input_range<const View> and std::ranges::common_range<const View> {
63-
return {*this, std::ranges::end(base_), count_};
70+
end() const requires std::ranges::input_range<const View> and std::ranges::common_range<
71+
const View> and different_from<Bound, std::unreachable_sentinel_t> {
72+
return {*this, std::ranges::end(base_), bound_};
6473
}
6574

6675
constexpr auto
67-
size() requires std::ranges::sized_range<View> {
68-
return std::ranges::empty(base_)
69-
? 0
70-
: std::ranges::size(base_) + static_cast<std::ranges::range_size_t<View>>(count_)
71-
- 1;
76+
size() requires
77+
std::ranges::sized_range<View> and different_from<Bound, std::unreachable_sentinel_t> {
78+
using Size = std::ranges::range_size_t<View>;
79+
if (std::ranges::empty(base_))
80+
return static_cast<Size>(0);
81+
return std::ranges::size(base_) + static_cast<Size>(bound_) - 1;
7282
}
7383
constexpr auto
74-
size() const requires std::ranges::sized_range<const View> {
75-
return std::ranges::empty(base_)
76-
? 0
77-
: std::ranges::size(base_)
78-
+ static_cast<std::ranges::range_size_t<const View>>(count_) - 1;
84+
size() const requires
85+
std::ranges::sized_range<const View> and different_from<Bound, std::unreachable_sentinel_t> {
86+
using Size = std::ranges::range_size_t<const View>;
87+
if (std::ranges::empty(base_))
88+
return static_cast<Size>(0);
89+
return std::ranges::size(base_) + static_cast<Size>(bound_) - 1;
7990
}
8091
};
8192

82-
template <class Range, class DifferenceType>
83-
trailing_view(Range&&, DifferenceType) -> trailing_view<std::views::all_t<Range>>;
93+
template <class Range>
94+
trailing_view(Range&&) -> trailing_view<std::views::all_t<Range>>;
95+
96+
template <class Range, class Bound>
97+
trailing_view(Range&&, Bound) -> trailing_view<std::views::all_t<Range>, Bound>;
8498

8599
template <class View>
86100
struct trailing_iterator_category {};
@@ -96,11 +110,14 @@ namespace strtpl {
96110
// clang-format on
97111
};
98112

99-
template <std::ranges::input_range View>
113+
// clang-format off
114+
template <std::ranges::input_range View, std::semiregular Bound>
100115
requires std::ranges::view<View>
116+
and (std::convertible_to<Bound, std::ranges::range_difference_t<View>> or std::same_as<Bound, std::unreachable_sentinel_t>)
101117
template <bool Const>
102-
struct trailing_view<View>::iterator
118+
struct trailing_view<View, Bound>::iterator
103119
: trailing_iterator_category<std::conditional_t<Const, const View, View>> {
120+
// clang-format on
104121
private:
105122
using Parent = std::conditional_t<Const, const trailing_view, trailing_view>;
106123
using Base = std::conditional_t<Const, const View, View>;
@@ -112,8 +129,9 @@ namespace strtpl {
112129

113130
constexpr bool
114131
accessible() const noexcept {
115-
return next_ != std::ranges::end(parent_->base())
116-
or (not std::ranges::empty(parent_->base()) and ncount_ < parent_->count());
132+
if (std::ranges::empty(parent_->base()))
133+
return false;
134+
return next_ != std::ranges::end(parent_->base()) or ncount_ != parent_->count();
117135
}
118136

119137
public:
@@ -203,14 +221,16 @@ namespace strtpl {
203221
friend constexpr bool
204222
operator==(const iterator& x,
205223
const iterator& y) requires std::equality_comparable<std::ranges::iterator_t<Base>> {
206-
return x.next_ == y.next_
207-
and (std::ranges::empty(x.parent_->base()) or x.ncount_ == y.ncount_);
224+
if (std::ranges::empty(x.parent_->base()) and std::ranges::empty(y.parent_->base()))
225+
return true;
226+
return x.next_ == y.next_ and x.ncount_ == y.ncount_;
208227
}
209228
friend constexpr bool
210229
operator==(const iterator& x, std::default_sentinel_t) requires
211230
std::equality_comparable<std::ranges::iterator_t<Base>> {
212-
return x.next_ == std::ranges::end(x.parent_->base())
213-
and (std::ranges::empty(x.parent_->base()) or x.ncount_ == x.parent_->count());
231+
if (std::ranges::empty(x.parent_->base()))
232+
return true;
233+
return x.next_ == std::ranges::end(x.parent_->base()) and x.ncount_ == x.parent_->count();
214234
}
215235

216236
friend constexpr std::pair<std::ranges::range_rvalue_reference_t<Base>,

tests/trailing_view/trailing_view.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ TEST_CASE("trailing_view", "[trailing_view]") {
172172
}
173173
}
174174

175+
TEST_CASE("trailing_view unreachable", "[trailing_view][unreachable]") {
176+
{
177+
std::vector<int> v{0, 1, 2, 3};
178+
auto tv = strtpl::trailing_view(v);
179+
auto it = std::ranges::begin(tv);
180+
for (const auto& y : v) {
181+
const auto& [x, last] = *it++;
182+
CHECK(x == y);
183+
CHECK(last == 0);
184+
}
185+
for (std::ptrdiff_t i = 1; i < 10; ++i) {
186+
const auto& [x, last] = *it++;
187+
CHECK(x == v.back());
188+
CHECK(last == i);
189+
}
190+
CHECK(it != std::ranges::end(tv));
191+
}
192+
}
193+
175194
TEST_CASE("trailing_view generators", "[trailing_view][generators]") {
176195
// clang-format off
177196
const auto v = GENERATE(

0 commit comments

Comments
 (0)