diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -42,6 +42,7 @@ __ranges/enable_borrowed_range.h __ranges/view.h __ranges/size.h + __ranges/ref_view.h __ranges/view_interface.h __split_buffer __std_stream diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/ref_view.h @@ -0,0 +1,90 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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___RANGES_REF_VIEW_H +#define _LIBCPP___RANGES_REF_VIEW_H + +#include <__config> +#include <__iterator/iterator_traits.h> +#include <__iterator/concepts.h> +#include <__ranges/access.h> +#include <__ranges/view_interface.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +// clang-format off +namespace ranges { + + template + concept borrowed_range = + range<_Range> && + (is_lvalue_reference_v<_Range> || enable_borrowed_range>); + + template + concept sized_range = range<_Range> && requires(_Range& t) { + ranges::size(t); + }; + + template + requires is_object_v<_Range> + class ref_view : public view_interface> { + _Range *__range = nullptr; + +public: + constexpr ref_view() noexcept = default; + + static void __test(_Range&); + static void __test(_Range&&) = delete; + + template + requires (!same_as<_Tp, ref_view>) && + convertible_to<_Tp, _Range&> && requires { __test(declval<_Tp>()); } + constexpr ref_view(_Tp&& __t) + : __range(_VSTD::addressof(static_cast<_Range&>(_VSTD::forward<_Tp>(__t)))) + {} + + constexpr _Range& base() const { return *__range; } + + constexpr iterator_t<_Range> begin() const { return ranges::begin(*__range); } + constexpr sentinel_t<_Range> end() const { return ranges::end(*__range); } + + constexpr bool empty() const + requires requires { ranges::empty(*__range); } + { return ranges::empty(*__range); } + + constexpr auto size() const requires sized_range<_Range> + { return ranges::size(*__range); } + + constexpr auto data() const requires contiguous_range<_Range> + { return ranges::data(*__range); } + }; + + template + ref_view(_Range&) -> ref_view<_Range>; + +} // namespace ranges + +// clang-format off + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_REF_VIEW_H diff --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h --- a/libcxx/include/__ranges/size.h +++ b/libcxx/include/__ranges/size.h @@ -89,7 +89,7 @@ template<__difference _Tp> [[nodiscard]] constexpr __integer_like auto operator()(_Tp&& __t) const noexcept(noexcept(ranges::end(__t) - ranges::begin(__t))) { - return __to_unsigned_like>>( + return make_unsigned_t>>( ranges::end(__t) - ranges::begin(__t)); } }; diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -83,6 +83,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/view.h> #include <__ranges/size.h> +#include <__ranges/ref_view.h> #include <__ranges/view_interface.h> #include // Required by the standard. #include // Required by the standard. diff --git a/libcxx/test/std/ranges/range.adaptors/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.ref.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.ref.view.pass.cpp @@ -0,0 +1,174 @@ +//===----------------------------------------------------------------------===// +// +// 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-no-concepts +// UNSUPPORTED: gcc-10 +// XFAIL: msvc && clang + +// std::ranges::size + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +namespace ranges = std::ranges; + +int globalBuff[8]; + +template +concept ValidRefView = requires { typename ranges::ref_view; }; + +struct Range { + int start = 0; + constexpr friend int* begin(Range const& range) { return globalBuff + range.start; } + constexpr friend int* end(Range const&) { return globalBuff + 8; } + constexpr friend int* begin(Range& range) { return globalBuff + range.start; } + constexpr friend int* end(Range&) { return globalBuff + 8; } +}; + +struct BeginOnly { + friend int* begin(BeginOnly const&); + friend int* begin(BeginOnly &); +}; + +static_assert( ValidRefView); +static_assert(!ValidRefView); +static_assert(!ValidRefView); + +static_assert(std::derived_from, ranges::view_interface>>); +static_assert(std::semiregular>); + +struct RangeConvertible { + operator Range& (); +}; + +struct RValueRangeConvertible { + operator Range&& (); +}; + +static_assert( std::is_constructible_v, Range&>); +static_assert( std::is_constructible_v, RangeConvertible>); +static_assert(!std::is_constructible_v, RValueRangeConvertible>); + +struct ForwardRange { + constexpr forward_iterator begin() const { return forward_iterator(globalBuff); } + constexpr forward_iterator end() const { return forward_iterator(globalBuff + 8); } +}; + +struct InputRange { + constexpr input_iterator begin() const { return input_iterator(globalBuff); } + constexpr int* end() const { return globalBuff + 8; } +}; + +bool operator==(input_iterator lhs, int* rhs) { return lhs.base() == rhs; } +bool operator==(int* lhs, input_iterator rhs) { return rhs.base() == lhs; } + +template +concept EmptyInvocable = requires (ranges::ref_view view) { view.empty(); }; + +template +concept SizeInvocable = requires (ranges::ref_view view) { view.size(); }; + +template +concept DataInvocable = requires (ranges::ref_view view) { view.data(); }; + +// Testing ctad. +static_assert(std::same_as())), + ranges::ref_view>); + +constexpr bool test() { + { + // ref_view::base + Range range; + ranges::ref_view view{range}; + assert(view.begin() == globalBuff); + view.base() = Range{2}; + assert(view.begin() == globalBuff + 2); + } + + { + // ref_view::begin + Range range1; + ranges::ref_view view1{range1}; + assert(view1.begin() == globalBuff); + + ForwardRange range2; + ranges::ref_view view2{range2}; + assert(view2.begin().base() == globalBuff); + + InputRange range3; + ranges::ref_view view3{range3}; + assert(view3.begin().base() == globalBuff); + } + + { + // ref_view::end + Range range1; + ranges::ref_view view1{range1}; + assert(view1.end() == globalBuff + 8); + + ForwardRange range2; + ranges::ref_view view2{range2}; + assert(view2.end().base() == globalBuff + 8); + + InputRange range3; + ranges::ref_view view3{range3}; + assert(view3.end() == globalBuff + 8); + } + + { + // ref_view::empty + Range range{8}; + ranges::ref_view view1{range}; + assert(view1.empty()); + + ForwardRange range2; + ranges::ref_view view2{range2}; + assert(!view2.empty()); + + static_assert(!EmptyInvocable); + } + + { + // ref_view::size + Range range1{8}; + ranges::ref_view view1{range1}; + assert(view1.size() == 0); + + Range range2{2}; + ranges::ref_view view2{range2}; + assert(view2.size() == 6); + + static_assert(!SizeInvocable); + } + + { + // ref_view::data + Range range1; + ranges::ref_view view1{range1}; + assert(view1.data() == globalBuff); + + Range range2{2}; + ranges::ref_view view2{range2}; + assert(view2.data() == globalBuff + 2); + + static_assert(!DataInvocable); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}