xref: /llvm-project/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp (revision 7918e624add98b409332c6996c89a70c74126994)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // <algorithm>
10 
11 // UNSUPPORTED: c++03, c++11, c++14, c++17
12 
13 // template<class T, class Proj = identity,
14 //          indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
15 //   constexpr const T&
16 //     ranges::clamp(const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {});
17 
18 #include <algorithm>
19 #include <cassert>
20 #include <concepts>
21 #include <functional>
22 #include <iterator>
23 #include <utility>
24 
25 template <class T, class Comp = std::ranges::less, class Proj = std::identity>
26 concept HasClamp =
27     requires(T&& val, T&& low, T&& high, Comp&& comp, Proj&& proj) {
28       std::ranges::clamp(std::forward<T>(val), std::forward<T>(low), std::forward<T>(high),
29           std::forward<Comp>(comp), std::forward<Proj>(proj));
30     };
31 
32 struct NoComp {};
33 struct CreateNoComp {
34   auto operator()(int) const { return NoComp(); }
35 };
36 
37 static_assert(HasClamp<int, std::ranges::less, std::identity>);
38 static_assert(!HasClamp<NoComp>);
39 static_assert(!HasClamp<int, NoComp>);
40 static_assert(!HasClamp<int, std::ranges::less, CreateNoComp>);
41 
42 struct EnsureValueCategoryComp {
43   constexpr bool operator()(const int&& x, const int&& y) const { return x < y; }
44   constexpr bool operator()(const int&& x, int& y) const { return x < y; }
45   constexpr bool operator()(int& x, const int&& y) const { return x < y; }
46   constexpr bool operator()(int& x, int& y) const { return x < y; }
47   constexpr bool operator()(std::same_as<const int&> auto&& x, std::same_as<const int&> auto&& y) const {
48     return x < y;
49   }
50 };
51 
52 constexpr bool test() {
53   { // low < val < high
54     int val = 2;
55     int low = 1;
56     int high = 3;
57     std::same_as<const int&> decltype(auto) ret = std::ranges::clamp(val, low, high);
58     assert(ret == 2);
59     assert(&ret == &val);
60   }
61 
62   { // low > val < high
63     assert(std::ranges::clamp(10, 20, 30) == 20);
64   }
65 
66   { // low < val > high
67     assert(std::ranges::clamp(15, 5, 10) == 10);
68   }
69 
70   { // low == val == high
71     int val = 10;
72     assert(&std::ranges::clamp(val, 10, 10) == &val);
73   }
74 
75   { // Check that a custom comparator works.
76     assert(std::ranges::clamp(10, 30, 20, std::ranges::greater{}) == 20);
77   }
78 
79   { // Check that a custom projection works.
80     struct S {
81       int i;
82       constexpr bool operator==(S const& other) const { return i == other.i; }
83     };
84 
85     auto val = S{10};
86     auto low = S{20};
87     auto high = S{30};
88     auto proj = [](S const& s) -> int const& { return s.i; };
89 
90     assert(std::ranges::clamp(val, low, high, std::less{}, proj) == low);
91   }
92 
93   { // Ensure that we respect the value category of the projection when calling the comparator.
94     // This additional example was provided by Tim Song in https://github.com/microsoft/STL/issues/3970#issuecomment-1685120958.
95     struct MoveProj {
96       constexpr int const&& operator()(int const& x) const { return std::move(x); }
97     };
98 
99     static_assert(std::indirect_strict_weak_order<EnsureValueCategoryComp, std::projected<const int*, MoveProj>>);
100 
101     assert(std::ranges::clamp(0, 1, 2, EnsureValueCategoryComp{}, MoveProj{}) == 1);
102   }
103 
104   { // Make sure we don't call the projection more than three times per [alg.clamp], see #64717
105     int counter              = 0;
106     auto projection_function = [&counter](const int value) -> int {
107       counter++;
108       return value;
109     };
110     assert(std::ranges::clamp(3, 2, 4, std::ranges::less{}, projection_function) == 3);
111 #if defined(_LIBCPP_HARDENING_MODE) && \
112       _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_EXTENSIVE && \
113       _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_DEBUG
114     assert(counter <= 3);
115 #endif
116   }
117 
118   return true;
119 }
120 
121 int main(int, char**) {
122   test();
123   static_assert(test());
124 
125   return 0;
126 }
127