diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -51,6 +51,7 @@ __ranges/enable_borrowed_range.h __ranges/enable_view.h __ranges/view_interface.h + __ranges/ref_view.h __ranges/size.h __ranges/subrange.h __split_buffer 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,78 @@ +// -*- 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/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/data.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) + +namespace ranges { + template + requires is_object_v<_Range> + class ref_view : public view_interface> { + _Range *__range_; + + static void __fun(_Range&); + static void __fun(_Range&&) = delete; + +public: + template + requires __different_from<_Tp, ref_view> && + convertible_to<_Tp, _Range&> && requires { __fun(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_); } + + // TODO: This needs to use contiguous_range. + constexpr auto data() const + requires contiguous_iterator> + { return ranges::data(*__range_); } + }; + + template + ref_view(_Range&) -> ref_view<_Range>; + +} // namespace ranges + +#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 b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -100,6 +100,7 @@ #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> +#include <__ranges/ref_view.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> 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,208 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::ref_view + +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +int globalBuff[8]; + +template +concept ValidRefView = requires { typename std::ranges::ref_view; }; + +struct Range { + int start = 0; + friend constexpr int* begin(Range const& range) { return globalBuff + range.start; } + friend constexpr int* end(Range const&) { return globalBuff + 8; } + friend constexpr int* begin(Range& range) { return globalBuff + range.start; } + friend constexpr 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( ValidRefView); + +static_assert(std::derived_from, std::ranges::view_interface>>); + +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 ConstConvertibleToLValueAndRValue { + operator Range& () const; + operator Range&& () const; +}; +static_assert( std::is_convertible_v>); +static_assert(!std::is_convertible_v>); +static_assert(!std::is_convertible_v>); + +struct ForwardRange { + constexpr forward_iterator begin() const { return forward_iterator(globalBuff); } + constexpr forward_iterator end() const { return forward_iterator(globalBuff + 8); } +}; + +struct Cpp17InputRange { + struct sentinel { + friend constexpr bool operator==(sentinel, cpp17_input_iterator iter) { return iter.base() == globalBuff + 8; } + friend constexpr std::ptrdiff_t operator-(sentinel, cpp17_input_iterator) { return -8; } + friend constexpr std::ptrdiff_t operator-(cpp17_input_iterator, sentinel) { return 8; } + }; + + constexpr cpp17_input_iterator begin() const { + return cpp17_input_iterator(globalBuff); + } + constexpr sentinel end() const { return {}; } +}; + +struct Cpp20InputRange { + struct sentinel { + friend constexpr bool operator==(sentinel, const cpp20_input_iterator &iter) { return iter.base() == globalBuff + 8; } + friend constexpr std::ptrdiff_t operator-(sentinel, const cpp20_input_iterator&) { return -8; } + }; + + constexpr cpp20_input_iterator begin() const { + return cpp20_input_iterator(globalBuff); + } + constexpr sentinel end() const { return {}; } +}; + +template<> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +template +concept EmptyIsInvocable = requires (std::ranges::ref_view view) { view.empty(); }; + +template +concept SizeIsInvocable = requires (std::ranges::ref_view view) { view.size(); }; + +template +concept DataIsInvocable = requires (std::ranges::ref_view view) { view.data(); }; + +// Testing ctad. +static_assert(std::same_as())), + std::ranges::ref_view>); + +constexpr bool test() { + { + // ref_view::base + Range range; + std::ranges::ref_view view{range}; + assert(view.begin() == globalBuff); + view.base() = Range{2}; + assert(view.begin() == globalBuff + 2); + } + + { + // ref_view::begin + Range range1; + std::ranges::ref_view view1 = range1; + assert(view1.begin() == globalBuff); + + ForwardRange range2; + std::ranges::ref_view view2 = range2; + assert(view2.begin().base() == globalBuff); + + Cpp17InputRange range3; + std::ranges::ref_view view3 = range3; + assert(view3.begin().base() == globalBuff); + + Cpp20InputRange range4; + std::ranges::ref_view view4 = range4; + assert(view4.begin().base() == globalBuff); + } + + { + // ref_view::end + Range range1; + std::ranges::ref_view view1 = range1; + assert(view1.end() == globalBuff + 8); + + ForwardRange range2; + std::ranges::ref_view view2 = range2; + assert(view2.end().base() == globalBuff + 8); + + Cpp17InputRange range3; + std::ranges::ref_view view3 = range3; + assert(view3.end() == cpp17_input_iterator(globalBuff + 8)); + + Cpp20InputRange range4; + std::ranges::ref_view view4 = range4; + assert(view4.end() == cpp20_input_iterator(globalBuff + 8)); + } + + { + // ref_view::empty + Range range{8}; + std::ranges::ref_view view1 = range; + assert(view1.empty()); + + ForwardRange range2; + std::ranges::ref_view view2 = range2; + assert(!view2.empty()); + + static_assert(!EmptyIsInvocable); + static_assert(!EmptyIsInvocable); + } + + { + // ref_view::size + Range range1{8}; + std::ranges::ref_view view1 = range1; + assert(view1.size() == 0); + + Range range2{2}; + std::ranges::ref_view view2 = range2; + assert(view2.size() == 6); + + static_assert(!SizeIsInvocable); + } + + { + // ref_view::data + Range range1; + std::ranges::ref_view view1 = range1; + assert(view1.data() == globalBuff); + + Range range2{2}; + std::ranges::ref_view view2 = range2; + assert(view2.data() == globalBuff + 2); + + static_assert(!DataIsInvocable); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}