diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -32,7 +32,7 @@ Read-only,includes,Hui Xie,`D130116 `,✅ Read-only,is_heap,Konstantin Varlamov,`D130547 `_,✅ Read-only,is_heap_until,Konstantin Varlamov,`D130547 `_,✅ -Read-only,clamp,Nikolas Klauser,`D126193 `_,Under review +Read-only,clamp,Nikolas Klauser,`D126193 `_,✅ Read-only,is_permutation,Nikolas Klauser,`D127194 `_,Under review Read-only,for_each,Nikolas Klauser,`D124332 `_,✅ Read-only,for_each_n,Nikolas Klauser,`D124332 `_,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -73,6 +73,7 @@ __algorithm/ranges_all_of.h __algorithm/ranges_any_of.h __algorithm/ranges_binary_search.h + __algorithm/ranges_clamp.h __algorithm/ranges_copy.h __algorithm/ranges_copy_backward.h __algorithm/ranges_copy_if.h diff --git a/libcxx/include/__algorithm/clamp.h b/libcxx/include/__algorithm/clamp.h --- a/libcxx/include/__algorithm/clamp.h +++ b/libcxx/include/__algorithm/clamp.h @@ -22,7 +22,7 @@ #if _LIBCPP_STD_VER > 14 template _LIBCPP_NODISCARD_EXT inline -_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +_LIBCPP_INLINE_VISIBILITY constexpr const _Tp& clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi, _Compare __comp) { @@ -33,7 +33,7 @@ template _LIBCPP_NODISCARD_EXT inline -_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +_LIBCPP_INLINE_VISIBILITY constexpr const _Tp& clamp(const _Tp& __v, const _Tp& __lo, const _Tp& __hi) { diff --git a/libcxx/include/__algorithm/ranges_clamp.h b/libcxx/include/__algorithm/ranges_clamp.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__algorithm/ranges_clamp.h @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_CLAMP_H +#define _LIBCPP___ALGORITHM_RANGES_CLAMP_H + +#include <__assert> +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/projected.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +namespace __clamp { +struct __fn { + + template > _Comp = ranges::less> + _LIBCPP_HIDE_FROM_ABI constexpr + const _Type& operator()(const _Type& __value, + const _Type& __low, + const _Type& __high, + _Comp __comp = {}, + _Proj __proj = {}) const { + _LIBCPP_ASSERT(bool(std::invoke(__comp, std::invoke(__proj, __high), std::invoke(__proj, __low))), + "Bad bounds passed to std::ranges::clamp"); + auto&& __val = std::invoke(__proj, __value); + if (std::invoke(__comp, __val, std::invoke(__proj, __low))) + return __low; + else if (std::invoke(__comp, std::invoke(__proj, __high), __val)) + return __high; + else + return __value; + } + +}; +} // namespace __clamp + +inline namespace __cpo { + inline constexpr auto clamp = __clamp::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +#endif // _LIBCPP___ALGORITHM_RANGES_CLAMP_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -593,6 +593,11 @@ constexpr borrowed_iterator_t ranges::replace_if(R&& r, Pred pred, const T& new_value, Proj proj = {}); // since C++20 + template> Comp = ranges::less> + constexpr const T& + ranges::clamp(const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {}); // since C++20 + template S1, input_iterator I2, sentinel_for S2, class Proj1 = identity, class Proj2 = identity, indirect_strict_weak_order, @@ -912,7 +917,7 @@ indirectly_copyable_storable, O>) constexpr unique_copy_result, O> unique_copy(R&& r, O result, C comp = {}, Proj proj = {}); // Since C++20 -} + } constexpr bool // constexpr in C++20 all_of(InputIterator first, InputIterator last, Predicate pred); @@ -1645,6 +1650,7 @@ #include <__algorithm/ranges_all_of.h> #include <__algorithm/ranges_any_of.h> #include <__algorithm/ranges_binary_search.h> +#include <__algorithm/ranges_clamp.h> #include <__algorithm/ranges_copy.h> #include <__algorithm/ranges_copy_backward.h> #include <__algorithm/ranges_copy_if.h> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -312,6 +312,7 @@ module ranges_all_of { private header "__algorithm/ranges_all_of.h" } module ranges_any_of { private header "__algorithm/ranges_any_of.h" } module ranges_binary_search { private header "__algorithm/ranges_binary_search.h" } + module ranges_clamp { private header "__algorithm/ranges_clamp.h" } module ranges_copy { private header "__algorithm/ranges_copy.h" } module ranges_copy_backward { private header "__algorithm/ranges_copy_backward.h" } module ranges_copy_if { private header "__algorithm/ranges_copy_if.h" } diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -110,6 +110,7 @@ #include <__algorithm/ranges_all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_all_of.h'}} #include <__algorithm/ranges_any_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_any_of.h'}} #include <__algorithm/ranges_binary_search.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_binary_search.h'}} +#include <__algorithm/ranges_clamp.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_clamp.h'}} #include <__algorithm/ranges_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy.h'}} #include <__algorithm/ranges_copy_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_backward.h'}} #include <__algorithm/ranges_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_if.h'}} diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.sorting/alg.clamp/ranges.clamp.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template> Comp = ranges::less> +// constexpr const T& +// ranges::clamp(const T& v, const T& lo, const T& hi, Comp comp = {}, Proj proj = {}); + +#include +#include +#include + +constexpr bool test() { + { // low < val < high + auto val = 20; + auto low = 10; + auto high = 30; + std::same_as decltype(auto) ret = std::ranges::clamp(val, low, high); + assert(ret == 20); + } + // val < low < high + assert(std::ranges::clamp(10, 20, 30) == 20); + + // low < high < val + assert(std::ranges::clamp(30, 10, 20) == 20); + + // check that the comparator is used + assert(std::ranges::clamp(10, 30, 20, std::ranges::greater{}) == 20); + + { // low = val = high + int val = 10; + assert(&std::ranges::clamp(val, 10, 10) == &val); + } + + { // check that the projection is used + struct S { + int i; + }; + + auto val = S{10}; + auto low = S{20}; + auto high = S{30}; + assert(&std::ranges::clamp(val, low, high, {}, &S::i) == &low); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}