Skip to content

Commit ce033fe

Browse files
Add a second cpp2::range constructor which takes 2 types, and deduce the common type for them (if available) (#1270)
* Add a `cpp2::range` ctor which takes 2 different types Use a CTAD deduction guide to deduce the `std::common_type` for both types (if a common one exists). This allows code like the following to compile without integer precision conversion warnings: ``` for 0 ..< vector.size() do (e) { ... } ``` since the integer literal 0 and the size expression evaluate as different signed/unsigned types. * Update regression tests due to line number changes * Merge results of rerunning regression on my machine --------- Signed-off-by: Herb Sutter <[email protected]> Co-authored-by: Herb Sutter <[email protected]>
1 parent b433693 commit ce033fe

File tree

40 files changed

+634
-30
lines changed

40 files changed

+634
-30
lines changed

include/cpp2util.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,11 @@ concept valid_custom_is_operator = predicate_member_fun<X, F, &F::op_is>
746746
|| brace_initializable_to<X, argument_of_op_is_t<F>>
747747
);
748748

749+
template <typename T, typename U>
750+
concept has_common_type = requires (T t, U u) {
751+
typename std::common_type_t<T, U>;
752+
};
753+
749754
//-----------------------------------------------------------------------
750755
//
751756
// General helpers
@@ -2437,6 +2442,19 @@ class range
24372442
}
24382443
}
24392444

2445+
// When the first & last types are different, use a CTAD deduction guide
2446+
// to find the `std::common_type` for them, if one exists. See below
2447+
// after the class definition for the deduction guide.
2448+
template <typename U>
2449+
requires has_common_type<T, U>
2450+
range(
2451+
T const& f,
2452+
U const& l,
2453+
bool include_last = false
2454+
)
2455+
: range(f, l, include_last)
2456+
{}
2457+
24402458
class iterator
24412459
{
24422460
TT first = T{};
@@ -2572,6 +2590,16 @@ class range
25722590
}
25732591
};
25742592

2593+
// CTAD deduction guide for the `range` constructor that takes two different types.
2594+
// Deduces the `std::common_type` for them, if one exists.
2595+
template <typename T, typename U>
2596+
requires has_common_type<T, U>
2597+
range(
2598+
T const& f,
2599+
U const& l,
2600+
bool include_last = false
2601+
) -> range<std::common_type_t<T, U>>;
2602+
25752603
template<typename T>
25762604
constexpr auto contains(range<T> const& r, T const& t)
25772605
-> bool

regression-tests/pure2-range-operators.cpp2

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ main: () = {
1313
std::cout << " (e)$ (v[e])$\n";
1414
}
1515

16+
std::cout << "\nAnd test the range when mixing signed & unsigned types:\n";
17+
for 0 ..< v.size() do (e) {
18+
std::cout << " (e)$ (v[e])$\n";
19+
}
20+
1621
all_about: std::list =
1722
( "Hokey", "Pokey" );
1823

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
pure2-last-use.cpp2:273:36: error: expected variable name or 'this' in lambda capture list
2+
public: std::add_pointer_t<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> g;
3+
^
4+
pure2-last-use.cpp2:329:2: error: expected '>'
5+
};
6+
^
7+
pure2-last-use.cpp2:273:30: note: to match this '<'
8+
public: std::add_pointer_t<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> g;
9+
^
10+
pure2-last-use.cpp2:344:16: error: no template named 'move_only_function' in namespace 'std'
11+
public: std::move_only_function<int()> b;
12+
~~~~~^
13+
pure2-last-use.cpp2:348:161: error: no member named 'move_only_function' in namespace 'std'
14+
CPP2_REQUIRES_ (std::is_convertible_v<CPP2_TYPEOF(a_), std::add_const_t<std::unique_ptr<int>>&> && std::is_convertible_v<CPP2_TYPEOF(b_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(c_), std::add_const_t<std::add_lvalue_reference_t<int>>&>) ;
15+
~~~~~^
16+
../../../include/cpp2util.h:10008:43: note: expanded from macro 'CPP2_REQUIRES_'
17+
#define CPP2_REQUIRES_(...) requires (__VA_ARGS__)
18+
^~~~~~~~~~~
19+
pure2-last-use.cpp2:348:188: error: expected expression
20+
CPP2_REQUIRES_ (std::is_convertible_v<CPP2_TYPEOF(a_), std::add_const_t<std::unique_ptr<int>>&> && std::is_convertible_v<CPP2_TYPEOF(b_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(c_), std::add_const_t<std::add_lvalue_reference_t<int>>&>) ;
21+
^
22+
pure2-last-use.cpp2:348:193: error: use of address-of-label extension outside of a function body
23+
CPP2_REQUIRES_ (std::is_convertible_v<CPP2_TYPEOF(a_), std::add_const_t<std::unique_ptr<int>>&> && std::is_convertible_v<CPP2_TYPEOF(b_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(c_), std::add_const_t<std::add_lvalue_reference_t<int>>&>) ;
24+
^
25+
pure2-last-use.cpp2:773:69: error: no template named 'move_only_function' in namespace 'std'
26+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
27+
~~~~~^
28+
pure2-last-use.cpp2:773:93: error: expected variable name or 'this' in lambda capture list
29+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
30+
^
31+
pure2-last-use.cpp2:773:156: error: expected unqualified-id
32+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
33+
^
34+
pure2-last-use.cpp2:773:160: error: expected '>'
35+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
36+
^
37+
pure2-last-use.cpp2:773:87: note: to match this '<'
38+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
39+
^
40+
pure2-last-use.cpp2:773:160: error: expected ')'
41+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
42+
^
43+
pure2-last-use.cpp2:773:17: note: to match this '('
44+
auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, std::move_only_function<int([[maybe_unused]] cpp2::impl::in<int> unnamed_param_1)> size) -> void;
45+
^
46+
pure2-last-use.cpp2:271:7: error: missing '}' at end of definition of 'issue_857_4'
47+
class issue_857_4 {
48+
^
49+
pure2-last-use.cpp2:905:1: note: still within definition of 'issue_857_4' here
50+
namespace captures {
51+
^
52+
pure2-last-use.cpp2:279:272: error: no member named 'move_only_function' in namespace 'std'
53+
requires (std::is_convertible_v<CPP2_TYPEOF(f_), std::add_const_t<std::add_pointer_t<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(g_), std::add_const_t<std::add_pointer_t<int(cpp2::impl::in<int> in_)>>&> && std::is_convertible_v<CPP2_TYPEOF(mf_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(mg_), std::add_const_t<std::move_only_function<int(cpp2::impl::in<int> in_)>>&>)
54+
~~~~~^
55+
pure2-last-use.cpp2:279:299: error: expected expression
56+
requires (std::is_convertible_v<CPP2_TYPEOF(f_), std::add_const_t<std::add_pointer_t<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(g_), std::add_const_t<std::add_pointer_t<int(cpp2::impl::in<int> in_)>>&> && std::is_convertible_v<CPP2_TYPEOF(mf_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(mg_), std::add_const_t<std::move_only_function<int(cpp2::impl::in<int> in_)>>&>)
57+
^
58+
pure2-last-use.cpp2:279:304: error: use of address-of-label extension outside of a function body
59+
requires (std::is_convertible_v<CPP2_TYPEOF(f_), std::add_const_t<std::add_pointer_t<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(g_), std::add_const_t<std::add_pointer_t<int(cpp2::impl::in<int> in_)>>&> && std::is_convertible_v<CPP2_TYPEOF(mf_), std::add_const_t<std::move_only_function<int()>>&> && std::is_convertible_v<CPP2_TYPEOF(mg_), std::add_const_t<std::move_only_function<int(cpp2::impl::in<int> in_)>>&>)
60+
^
61+
pure2-last-use.cpp2:278:14: error: out-of-line definition of 'issue_857_4' does not match any declaration in 'issue_857_4'
62+
issue_857_4::issue_857_4(auto&& f_, auto&& g_, auto&& mf_, auto&& mg_)
63+
^~~~~~~~~~~
64+
pure2-last-use.cpp2:281:272: error: member initializer 'g' does not name a non-static data member or base class
65+
, g{ CPP2_FORWARD(g_) }
66+
^~~~~~~~~~~~~~~~~~~~~
67+
pure2-last-use.cpp2:282:272: error: member initializer 'mf' does not name a non-static data member or base class
68+
, mf{ CPP2_FORWARD(mf_) }
69+
^~~~~~~~~~~~~~~~~~~~~~~
70+
pure2-last-use.cpp2:283:272: error: member initializer 'mg' does not name a non-static data member or base class
71+
, mg{ CPP2_FORWARD(mg_) }{}
72+
^~~~~~~~~~~~~~~~~~~~~~~
73+
fatal error: too many errors emitted, stopping now [-ferror-limit=]
74+
20 errors generated.

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ And from indexes 1..=5 they are:
1515
4 Elephant
1616
5 Flicker
1717

18+
And test the range when mixing signed & unsigned types:
19+
0 Aardvark
20+
1 Baboon
21+
2 Cat
22+
3 Dolphin
23+
4 Elephant
24+
5 Flicker
25+
6 Grue
26+
7 Wumpus
27+
1828
Make sure non-random-access iterators work:
1929
Hokey
2030
Pokey
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(1103) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector<int>]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
1+
../../../include/cpp2util.h(1108) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector<int>]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(915) : Bounds safety violation
1+
../../../include/cpp2util.h(920) : Bounds safety violation
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(915) : Contract violation: fill: value must contain at least count elements
1+
../../../include/cpp2util.h(920) : Contract violation: fill: value must contain at least count elements
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
sending error to my framework... [dynamic null dereference attempt detected]
2-
from source location: ../../../include/cpp2util.h(994) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&]
2+
from source location: ../../../include/cpp2util.h(999) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(994) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected<int, bool>]: Null safety violation: std::expected has an unexpected value
1+
../../../include/cpp2util.h(999) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected<int, bool>]: Null safety violation: std::expected has an unexpected value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(994) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional<int>]: Null safety violation: std::optional does not contain a value
1+
../../../include/cpp2util.h(999) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional<int>]: Null safety violation: std::optional does not contain a value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(994) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr<int>]: Null safety violation: std::shared_ptr is empty
1+
../../../include/cpp2util.h(999) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr<int>]: Null safety violation: std::shared_ptr is empty
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
../../../include/cpp2util.h(994) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr<int>]: Null safety violation: std::unique_ptr is empty
1+
../../../include/cpp2util.h(999) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr<int>]: Null safety violation: std::unique_ptr is empty

0 commit comments

Comments
 (0)