Skip to content

Commit 08a5ebb

Browse files
committed
Support .sum and .contains for ..= and ..< ranges
As nonmembers, UFCS will find them Examples: std::cout << "((1 ..= 20).sum())$\n"; // prints 210 std::cout << "((1 ..< 20).sum())$\n"; // prints 190 std::cout << "((1 ..= 20).contains(0))$\n"; // prints false std::cout << "((1 ..= 20).contains(1))$\n"; // prints true
1 parent ecd141d commit 08a5ebb

16 files changed

+326
-96
lines changed

include/cpp2util.h

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,14 @@
280280
#include <limits>
281281
#include <map>
282282
#include <memory>
283+
#include <numeric>
283284
#include <new>
284285
#include <random>
285286
#include <optional>
286287
#if defined(CPP2_USE_SOURCE_LOCATION)
287288
#include <source_location>
288289
#endif
290+
#include <ranges>
289291
#include <set>
290292
#include <span>
291293
#include <sstream>
@@ -330,6 +332,19 @@
330332
#define CPP2_CONTINUE_BREAK(NAME) goto CONTINUE_##NAME; CONTINUE_##NAME: continue; goto BREAK_##NAME; BREAK_##NAME: break;
331333
// these redundant goto's to avoid 'unused label' warnings
332334

335+
#if defined(__clang_major__)
336+
constexpr auto CPP2_CLANG_VER = __clang_major__ * 100 + __clang_minor__;
337+
constexpr auto CPP2_MSVC_VER = 0;
338+
constexpr auto CPP2_GCC_VER = 0;
339+
#elif defined(_MSC_VER)
340+
constexpr auto CPP2_CLANG_VER = 0;
341+
constexpr auto CPP2_MSVC_VER = _MSC_VER;
342+
constexpr auto CPP2_GCC_VER = 0;
343+
#elif defined(__GNUC__)
344+
constexpr auto CPP2_CLANG_VER = 0;
345+
constexpr auto CPP2_MSVC_VER = 0;
346+
constexpr auto CPP2_GCC_VER = __GNUC__ * 100 + __GNUC_MINOR__;
347+
#endif
333348

334349
#if defined(_MSC_VER)
335350
// MSVC can't handle 'inline constexpr' yet in all cases
@@ -2439,8 +2454,8 @@ class range
24392454
if constexpr (std::integral<TT>) {
24402455
if (last == std::numeric_limits<TT>::max()) {
24412456
throw std::runtime_error(
2442-
"range with last == numeric_limits<T>::max() will "
2443-
"overflow");
2457+
"range with last == numeric_limits<T>::max() will overflow"
2458+
);
24442459
}
24452460
}
24462461
++last;
@@ -2476,6 +2491,8 @@ class range
24762491

24772492
auto operator<=>(iterator const&) const = default;
24782493

2494+
operator typename range<const T>::iterator() const { return {first, last, curr}; }
2495+
24792496
// In this section, we don't use relational comparisons so that
24802497
// this works when T is a less-powerful-than-random-access iterator
24812498
//
@@ -2530,12 +2547,39 @@ class range
25302547
auto operator- (iterator that) const -> difference_type { return that.curr - curr; }
25312548
};
25322549

2533-
auto begin() const -> iterator { return iterator{ first, last, first }; }
2534-
auto end() const -> iterator { return iterator{ first, last, last }; }
2535-
auto cbegin() const -> iterator { return begin(); }
2536-
auto cend() const -> iterator { return end(); }
2550+
using const_iterator = typename range<const T>::iterator;
2551+
2552+
auto cbegin() const -> const_iterator { return begin(); }
2553+
auto cend() const -> const_iterator { return end(); }
2554+
auto begin() const -> const_iterator { return iterator{ first, last, first }; }
2555+
auto end() const -> const_iterator { return iterator{ first, last, last }; }
2556+
auto begin() -> iterator { return iterator{ first, last, first }; }
2557+
auto end() -> iterator { return iterator{ first, last, last }; }
25372558
auto size() const -> std::size_t { return unsafe_narrow<std::size_t>(ssize()); }
25382559
auto ssize() const -> std::ptrdiff_t { return last - first; }
2560+
auto empty() const -> bool { return first == last; }
2561+
2562+
auto front() const -> T {
2563+
type_safety.enforce(!empty());
2564+
if constexpr (std::is_same_v<T, TT>) {
2565+
return first;
2566+
}
2567+
else {
2568+
return unsafe_narrow<T>(first);
2569+
}
2570+
}
2571+
2572+
auto back() const -> T {
2573+
type_safety.enforce(!empty());
2574+
if constexpr (std::is_same_v<T, TT>) {
2575+
auto ret = last;
2576+
return --ret;
2577+
}
2578+
else {
2579+
auto ret = unsafe_narrow<T>(last);
2580+
return --ret;
2581+
}
2582+
}
25392583

25402584
auto operator[](difference_type i) const -> T
25412585
{
@@ -2553,6 +2597,23 @@ class range
25532597
}
25542598
};
25552599

2600+
template<typename T>
2601+
auto contains(range<T> const& r, T const& t)
2602+
-> bool
2603+
{
2604+
if (r.empty()) {
2605+
return false;
2606+
}
2607+
return r.front() <= t && t <= r.back();
2608+
}
2609+
2610+
template<typename T>
2611+
auto sum(range<T> const& r)
2612+
-> T
2613+
{
2614+
return std::accumulate(r.begin(), r.end(), T{});
2615+
}
2616+
25562617

25572618
//-----------------------------------------------------------------------
25582619
//

regression-tests/pure2-range-operators.cpp2

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
main: () = {
21

2+
main: () = {
33
v: std::vector =
44
( "Aardvark", "Baboon", "Cat", "Dolphin", "Elephant", "Flicker", "Grue", "Wumpus" );
55

@@ -21,4 +21,36 @@ main: () = {
2121
std::cout << " (e*)$\n";
2222
}
2323

24+
std::cout << "\nMake sure .sum works:\n";
25+
std::cout << "((1 ..= 20).sum())$\n"; // prints 210
26+
std::cout << "((1 ..< 20).sum())$\n"; // prints 190
27+
28+
std::cout << "\nMake sure .contains works:\n";
29+
std::cout << "((1 ..= 20).contains( 0))$\n"; // prints false
30+
std::cout << "((1 ..= 20).contains( 1))$\n"; // prints true
31+
std::cout << "((1 ..= 20).contains(19))$\n"; // prints true
32+
std::cout << "((1 ..= 20).contains(20))$\n"; // prints true
33+
std::cout << "((1 ..= 20).contains(21))$\n"; // prints false
34+
std::cout << "((1 ..< 20).contains( 0))$\n"; // prints false
35+
std::cout << "((1 ..< 20).contains( 1))$\n"; // prints true
36+
std::cout << "((1 ..< 20).contains(19))$\n"; // prints true
37+
std::cout << "((1 ..< 20).contains(20))$\n"; // prints false
38+
std::cout << "((1 ..< 20).contains(21))$\n"; // prints false
39+
40+
// Only run these parts on implementations that support views::take
41+
// : <V: bool = (CPP2_GCC_VER > 1400 || CPP2_CLANG_VER > 1600 || CPP2_MSVC_VER > 1920)> () = {
42+
// if constexpr V
43+
// {
44+
// using std::views::_ ;
45+
46+
// std::cout << "\nMake sure views::take works:\n";
47+
// for (1 ..= 100).take(5) do (e) {
48+
// std::cout << e << " ";
49+
// } // prints: 1 2 3 4 5
50+
51+
// for (2 ..< 5).take(5) do (e) {
52+
// std::cout << e << " ";
53+
// } // prints: 2 3 4
54+
// }
55+
// } ();
2456
}

regression-tests/test-results/clang-12-c++20/pure2-range-operators.cpp.execution

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,19 @@ And from indexes 1..=5 they are:
1818
Make sure non-random-access iterators work:
1919
Hokey
2020
Pokey
21+
22+
Make sure .sum works:
23+
210
24+
190
25+
26+
Make sure .contains works:
27+
false
28+
true
29+
true
30+
true
31+
false
32+
false
33+
true
34+
true
35+
false
36+
false

regression-tests/test-results/gcc-10-c++20/mixed-type-safety-1.cpp.output

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/usr/bin/ld: /tmp/ccoR2zYT.o: in function `main':
1+
/usr/bin/ld: /tmp/ccidvLpf.o: in function `main':
22
mixed-type-safety-1.cpp:(.text+0x327): undefined reference to `void print<std::integral_constant<bool, false> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::integral_constant<bool, false> const&)'
33
/usr/bin/ld: mixed-type-safety-1.cpp:(.text+0x388): undefined reference to `void print<std::integral_constant<bool, true> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::integral_constant<bool, true> const&)'
44
/usr/bin/ld: mixed-type-safety-1.cpp:(.text+0x422): undefined reference to `void print<std::integral_constant<bool, true> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::integral_constant<bool, true> const&)'
Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,66 @@
11
In file included from pure2-default-arguments.cpp:7:
22
../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context
3-
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return operator_as<12>(x); }
4-
| ^~~
3+
2086 |
4+
| ^
55
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
6-
2047 | template<typename T, typename... Ts>
7-
| ^~~
6+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
7+
| ^~~~~~~~~~~
88
../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’
9-
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return operator_as<12>(x); }
10-
| ^~~~~~~~~~~~
9+
2086 |
10+
| ^
1111
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
12-
2107 | { return x.type() == Typeid<T>(); }
13-
| ^~~~~~~~~~~~~~~~~~
12+
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
13+
| ^~~~~~~~~~~~~~~~~~~~~~~~
1414
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
15-
2137 | // as
15+
2137 | }
1616
| ^
1717
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
1818
../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context
19-
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return operator_as<12>(x); }
20-
| ^~~~~~
19+
2086 |
20+
| ^
2121
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
22-
2047 | template<typename T, typename... Ts>
23-
| ^~~
22+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
23+
| ^~~~~~~~~~~
2424
../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’
25-
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<12>(x)), T >) { if (x.index() == 12) return operator_as<12>(x); }
26-
| ^~~~~~~~~~~~
25+
2086 |
26+
| ^
2727
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
28-
2107 | { return x.type() == Typeid<T>(); }
29-
| ^~~~~~~~~~~~~~~~~~
28+
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
29+
| ^~~~~~~~~~~~~~~~~~~~~~~~
3030
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
31-
2137 | // as
31+
2137 | }
3232
| ^
3333
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
3434
../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context
35-
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return operator_as<13>(x); }
36-
| ^~~
35+
2087 | template<typename T, typename... Ts>
36+
| ^
3737
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
38-
2047 | template<typename T, typename... Ts>
39-
| ^~~
38+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
39+
| ^~~~~~~~~~~
4040
../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’
41-
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return operator_as<13>(x); }
42-
| ^~~~~~~~~~~~
41+
2087 | template<typename T, typename... Ts>
42+
| ^
4343
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
44-
2107 | { return x.type() == Typeid<T>(); }
45-
| ^~~~~~~~~~~~~~~~~~
44+
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
45+
| ^~~~~~~~~~~~~~~~~~~~~~~~
4646
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
47-
2137 | // as
47+
2137 | }
4848
| ^
4949
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
5050
../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context
51-
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return operator_as<13>(x); }
52-
| ^~~~~~
51+
2087 | template<typename T, typename... Ts>
52+
| ^
5353
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
54-
2047 | template<typename T, typename... Ts>
55-
| ^~~
54+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 8>(x)), T >) { if (x.index() == 8) return operator_as<8>(x); }
55+
| ^~~~~~~~~~~
5656
../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’
57-
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<13>(x)), T >) { if (x.index() == 13) return operator_as<13>(x); }
58-
| ^~~~~~~~~~~~
57+
2087 | template<typename T, typename... Ts>
58+
| ^
5959
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
60-
2107 | { return x.type() == Typeid<T>(); }
61-
| ^~~~~~~~~~~~~~~~~~
60+
2107 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
61+
| ^~~~~~~~~~~~~~~~~~~~~~~~
6262
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
63-
2137 | // as
63+
2137 | }
6464
| ^
6565
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
6666
pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared

regression-tests/test-results/gcc-10-c++20/pure2-range-operators.cpp.execution

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,19 @@ And from indexes 1..=5 they are:
1818
Make sure non-random-access iterators work:
1919
Hokey
2020
Pokey
21+
22+
Make sure .sum works:
23+
210
24+
190
25+
26+
Make sure .contains works:
27+
false
28+
true
29+
true
30+
true
31+
false
32+
false
33+
true
34+
true
35+
false
36+
false
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:91&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:91 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation
1+
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:105&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:105 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
In file included from mixed-bugfix-for-ufcs-non-local.cpp:6:
22
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
3-
2100 | //
3+
2100 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return operator_as<11>(x); }
44
| ^
55
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
6-
2137 | // as
6+
2137 | }
77
| ^
88
mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
99
mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid
1010
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
11-
2100 | //
11+
2100 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return operator_as<11>(x); }
1212
| ^
1313
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
14-
2137 | // as
14+
2137 | }
1515
| ^
1616
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
1717
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid
1818
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
19-
2100 | //
19+
2100 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return operator_as<11>(x); }
2020
| ^
2121
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
22-
2137 | // as
22+
2137 | }
2323
| ^
2424
mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
2525
mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid
2626
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
27-
2100 | //
27+
2100 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return operator_as<11>(x); }
2828
| ^
2929
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
30-
2137 | // as
30+
2137 | }
3131
| ^
3232
mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
3333
mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid
3434
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
35-
2100 | //
35+
2100 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<11>(x)), T >) { if (x.index() == 11) return operator_as<11>(x); }
3636
| ^
3737
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
38-
2137 | // as
38+
2137 | }
3939
| ^
4040
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
4141
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid

regression-tests/test-results/gcc-14-c++2b/pure2-range-operators.cpp.execution

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,19 @@ And from indexes 1..=5 they are:
1818
Make sure non-random-access iterators work:
1919
Hokey
2020
Pokey
21+
22+
Make sure .sum works:
23+
210
24+
190
25+
26+
Make sure .contains works:
27+
false
28+
true
29+
true
30+
true
31+
false
32+
false
33+
true
34+
true
35+
false
36+
false

0 commit comments

Comments
 (0)