Skip to content

Commit 4159905

Browse files
committed
Support integer ranges that use all values of the type
Closes #1180 These now work correctly: // Prints 0 to 255 inclusive MIN: u8 = 0; MAX: u8 = 255; for MIN..=MAX do (i) { std::cout << i as u32 << " "; } // Not an endless loop for 0 ..= std::numeric_limits<i32>::max() do (i) { /*...*/ }
1 parent fc230ff commit 4159905

File tree

3 files changed

+109
-62
lines changed

3 files changed

+109
-62
lines changed

include/cpp2util.h

Lines changed: 107 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,115 +2263,162 @@ inline auto make_args(int argc, char** argv) -> args
22632263
//
22642264
// range: a range of [begin, end) or [first, last]
22652265
//
2266+
// TT is the type we actually store for 'first' and 'last'.
2267+
//
2268+
// If T is integral, store a widened representation to ensure that
2269+
// the past-the-end value is representable even if [first,last] are
2270+
// numeric_limits<T> [min,max].
2271+
//
2272+
// This lets us represent all ranges as half-open ranges using just
2273+
// 'first' and (possibly-adjusted-by-one) 'last' without any extra
2274+
// data or a Closed parameter etc. = single simpler implementation.
2275+
//
22662276
//-----------------------------------------------------------------------
22672277
//
22682278
template<typename T>
2269-
struct range
2279+
class range
22702280
{
2281+
using TT = std::conditional_t<
2282+
std::is_integral_v<T>,
2283+
std::conditional_t<
2284+
std::is_signed_v<T>,
2285+
std::ptrdiff_t,
2286+
std::size_t
2287+
>,
2288+
T
2289+
>;
2290+
2291+
TT first;
2292+
TT last;
2293+
2294+
public:
22712295
using difference_type = std::ptrdiff_t;
2272-
using value_type = T;
2273-
using pointer = T*;
2274-
using reference = T&;
2296+
using value_type = T;
2297+
using pointer = T*;
2298+
using reference = T&;
22752299

22762300
range(
2277-
T const& f,
2278-
T const& l,
2279-
bool include_last = false
2301+
T const& f,
2302+
std::type_identity_t<T> const& l,
2303+
bool include_last = false
22802304
)
2305+
// For smaller-than-size_t/ptrdiff_t numeric types, these will widen
22812306
: first{ f }
22822307
, last{ l }
22832308
{
2309+
// Represent all ranges as half-open; after this we can forget the flag
22842310
if (include_last) {
22852311
++last;
22862312
}
22872313
}
22882314

2289-
// If T is numeric, use explicit narrowing to avoid compiler warnings
2290-
static auto inc_by(T& t, difference_type i) -> T&
2291-
{
2292-
if constexpr (std::integral<T>) {
2293-
return t += unsafe_narrow<T>(i);
2294-
}
2295-
else {
2296-
return t += i;
2297-
}
2298-
}
2299-
23002315
class iterator
23012316
{
2317+
TT first = T{};
2318+
TT last = T{};
2319+
TT curr = T{};
2320+
2321+
// Helper type trait to check for the existence of iterator_category
2322+
template <typename I, typename = void>
2323+
struct range_iterator_category {
2324+
using tag = std::random_access_iterator_tag;
2325+
};
2326+
template <typename I>
2327+
struct range_iterator_category<I, std::void_t<typename std::iterator_traits<I>::iterator_category>> {
2328+
using tag = typename std::iterator_traits<I>::iterator_category;
2329+
};
2330+
23022331
public:
23032332
using difference_type = std::ptrdiff_t;
23042333
using value_type = T;
23052334
using pointer = T*;
23062335
using reference = T&;
2307-
using iterator_category = std::random_access_iterator_tag;
2336+
using iterator_category = typename range_iterator_category<T>::tag;
23082337

23092338
iterator() { }
23102339

2311-
iterator(T const& f, T const& l, T start) : first{ f }, last{ l }, curr{ start } {}
2340+
iterator(TT const& f, TT const& l, TT start) : first{ f }, last{ l }, curr{ start } {}
23122341

23132342
auto operator<=>(iterator const&) const = default;
23142343

2315-
auto operator*() const -> T {
2316-
if (curr != last) { return curr; }
2317-
else { return T{}; }
2344+
// In this section, we don't use relational comparisons so that
2345+
// this works when T is a less-powerful-than-random-access iterator
2346+
//
2347+
auto operator*() const -> T
2348+
{
2349+
if (curr != last) {
2350+
if constexpr (std::is_same_v<T, TT>) {
2351+
return curr;
2352+
}
2353+
else {
2354+
return unsafe_narrow<T>(curr);
2355+
}
2356+
}
2357+
else {
2358+
return T{};
2359+
}
23182360
}
23192361

2320-
auto operator++() -> iterator& { if (curr != last ) { ++curr; } return *this; }
2321-
auto operator--() -> iterator& { if (curr != first) { --curr; } return *this; }
2322-
auto operator++(int) -> iterator { auto old = *this; ++*this; return old; }
2323-
auto operator--(int) -> iterator { auto old = *this; ++*this; return old; }
2362+
auto operator++() -> iterator& { if (curr != last ) { ++curr; } return *this; }
2363+
auto operator--() -> iterator& { if (curr != first) { --curr; } return *this; }
2364+
auto operator++(int) -> iterator { auto old = *this; ++*this; return old; }
2365+
auto operator--(int) -> iterator { auto old = *this; ++*this; return old; }
23242366

2325-
// And now all the random-access operations (valid if T is random-access)
2367+
// And now all the random-access operations which can use relational
2368+
// comparisons (these functions are valid if T is random-access)
23262369
//
23272370
auto operator[](difference_type i) const -> T {
2328-
if (curr + i != last) { return curr + i; }
2329-
else { return T{}; }
2371+
if (curr + i != last) {
2372+
if constexpr (std::is_same_v<T, TT>) {
2373+
return curr + i;
2374+
}
2375+
else {
2376+
return unsafe_narrow<T>(curr + i);
2377+
}
2378+
}
2379+
else {
2380+
return T{};
2381+
}
23302382
}
23312383

2332-
auto operator+=(difference_type i) -> iterator& { if (curr + i <= last ) { inc_by(curr, i); } else { curr = last; } return *this; }
2333-
auto operator-=(difference_type i) -> iterator& { if (curr - i >= first) { inc_by(curr, -i); } else { curr = first; } return *this; }
2384+
auto operator+=(difference_type i) -> iterator&
2385+
{ if (curr + i <= last ) { curr += i; } else { curr = last; } return *this; }
2386+
auto operator-=(difference_type i) -> iterator&
2387+
{ if (curr - i >= first) { curr -= i; } else { curr = first; } return *this; }
23342388

23352389
friend
2336-
auto operator+ (difference_type i, iterator const& this_) -> iterator { auto ret = *this_; return ret += i; }
2390+
auto operator+ (difference_type i, iterator const& iter) -> iterator
2391+
{ auto ret = *iter; return ret += i; }
23372392

23382393
auto operator+ (difference_type i ) const -> iterator { auto ret = *this; return ret += i; }
23392394
auto operator- (difference_type i ) const -> iterator { auto ret = *this; return ret -= i; }
23402395
auto operator- (iterator that) const -> difference_type { return that.curr - curr; }
2341-
2342-
//auto operator+(difference_type i) -> iterator {
2343-
// if (i > 0) { return { first, last, std::min(curr + i, last) }; }
2344-
// else { return { first, last, std::max(curr + i, 0) }; }
2345-
//}
2346-
//auto operator- (difference_type i) -> iterator { return operator+(-i); }
2347-
2348-
private:
2349-
T first = T{};
2350-
T last = T{};
2351-
T curr = T{};
23522396
};
23532397

2354-
auto begin() const -> iterator { return iterator{ first, last, first }; }
2355-
auto end() const -> iterator { return iterator{ first, last, last }; }
2356-
auto cbegin() const -> iterator { return begin(); }
2357-
auto cend() const -> iterator { return end(); }
2358-
auto size() const -> std::size_t { return unsafe_narrow<std::size_t>(ssize()); }
2359-
auto ssize() const -> int { return last - first; }
2398+
auto begin() const -> iterator { return iterator{ first, last, first }; }
2399+
auto end() const -> iterator { return iterator{ first, last, last }; }
2400+
auto cbegin() const -> iterator { return begin(); }
2401+
auto cend() const -> iterator { return end(); }
2402+
auto size() const -> std::size_t { return unsafe_narrow<std::size_t>(ssize()); }
2403+
auto ssize() const -> std::ptrdiff_t { return last - first; }
23602404

2361-
auto operator[](difference_type i) const {
2362-
if (0 <= i && i < ssize()) { return first + i; }
2363-
else { return T{}; }
2405+
auto operator[](difference_type i) const -> T
2406+
{
2407+
if (0 <= i && i < ssize()) {
2408+
if constexpr (std::is_same_v<T, TT>) {
2409+
return first + i;
2410+
}
2411+
else {
2412+
return unsafe_narrow<T>(first + i);
2413+
}
2414+
}
2415+
else {
2416+
return T{};
2417+
}
23642418
}
2365-
2366-
T first;
2367-
T last;
23682419
};
23692420

23702421

2371-
template<typename T, typename U>
2372-
range(T, U, bool = false) -> range<std::common_type_t<T, U>>;
2373-
2374-
23752422
//-----------------------------------------------------------------------
23762423
//
23772424
// alien_memory: memory typed as T but that is outside C++ and that the

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.7.1 Build 9724:1613
2+
cppfront compiler v0.7.1 Build 9724:2021
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"9724:1613"
1+
"9724:2021"

0 commit comments

Comments
 (0)