Skip to content

Commit 16822ed

Browse files
committed
as_const_view, views::as_const
includes testing
1 parent 6fd3330 commit 16822ed

File tree

13 files changed

+882
-0
lines changed

13 files changed

+882
-0
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ set(files
636636
__random/weibull_distribution.h
637637
__ranges/access.h
638638
__ranges/all.h
639+
__ranges/as_const_view.h
639640
__ranges/as_rvalue_view.h
640641
__ranges/chunk_by_view.h
641642
__ranges/common_view.h
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___RANGES_AS_CONST_VIEW_H
11+
#define _LIBCPP___RANGES_AS_CONST_VIEW_H
12+
13+
#include <__concepts/constructible.h>
14+
#include <__iterator/concepts.h>
15+
#include <__ranges/all.h>
16+
#include <__ranges/concepts.h>
17+
#include <__ranges/const_access.h>
18+
#include <__ranges/empty_view.h>
19+
#include <__ranges/range_adaptor.h>
20+
#include <__ranges/size.h>
21+
#include <__ranges/view_interface.h>
22+
#include <__type_traits/is_specialization.h>
23+
#include <__utility/auto_cast.h>
24+
#include <__utility/move.h>
25+
#include <cstddef>
26+
#include <span>
27+
28+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
29+
# pragma GCC system_header
30+
#endif
31+
32+
_LIBCPP_PUSH_MACROS
33+
#include <__undef_macros>
34+
35+
_LIBCPP_BEGIN_NAMESPACE_STD
36+
37+
#if _LIBCPP_STD_VER >= 23
38+
39+
namespace ranges {
40+
template <input_range _View>
41+
requires view<_View>
42+
class as_const_view : public view_interface<as_const_view<_View>> {
43+
_LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
44+
45+
public:
46+
_LIBCPP_HIDE_FROM_ABI as_const_view()
47+
requires default_initializable<_View>
48+
= default;
49+
_LIBCPP_HIDE_FROM_ABI constexpr explicit as_const_view(_View __base) : __base_(std::move(__base)) {}
50+
51+
_LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
52+
requires copy_constructible<_View>
53+
{
54+
return __base_;
55+
}
56+
57+
_LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
58+
59+
_LIBCPP_HIDE_FROM_ABI constexpr auto begin()
60+
requires(!__simple_view<_View>)
61+
{
62+
return ranges::cbegin(__base_);
63+
}
64+
65+
_LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
66+
requires range<const _View>
67+
{
68+
return ranges::cbegin(__base_);
69+
}
70+
71+
_LIBCPP_HIDE_FROM_ABI constexpr auto end()
72+
requires(!__simple_view<_View>)
73+
{
74+
return ranges::cend(__base_);
75+
}
76+
77+
_LIBCPP_HIDE_FROM_ABI constexpr auto end() const
78+
requires range<const _View>
79+
{
80+
return ranges::cend(__base_);
81+
}
82+
83+
_LIBCPP_HIDE_FROM_ABI constexpr auto size()
84+
requires sized_range<_View>
85+
{
86+
return ranges::size(__base_);
87+
}
88+
89+
_LIBCPP_HIDE_FROM_ABI constexpr auto size() const
90+
requires sized_range<const _View>
91+
{
92+
return ranges::size(__base_);
93+
}
94+
};
95+
96+
template <class _Range>
97+
as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
98+
99+
template <class _Tp>
100+
inline constexpr bool enable_borrowed_range<as_const_view<_Tp>> = enable_borrowed_range<_Tp>;
101+
102+
namespace views {
103+
namespace __as_const {
104+
105+
template <class _Tp>
106+
concept __has_type = requires { typename _Tp::type; };
107+
108+
template <class _Tp>
109+
struct __empty_view_case {};
110+
template <class _Tp>
111+
struct __empty_view_case<empty_view<_Tp>> {
112+
using type = const _Tp;
113+
};
114+
115+
template <class _Tp>
116+
struct __span_case {};
117+
template <class _Tp, size_t _Extent>
118+
struct __span_case<span<_Tp, _Extent>> {
119+
using type = span<const _Tp, _Extent>;
120+
};
121+
122+
template <class _Tp>
123+
struct __ref_view_case {};
124+
template <class _Tp>
125+
requires constant_range<const _Tp>
126+
struct __ref_view_case<ref_view<_Tp>> {
127+
using type = const _Tp&;
128+
};
129+
130+
template <class _Tp>
131+
struct __constant_range_case {};
132+
template <class _Tp>
133+
requires constant_range<const _Tp> && (!view<_Tp>)
134+
struct __constant_range_case<_Tp> {
135+
using type = const _Tp&;
136+
};
137+
138+
struct __fn : __range_adaptor_closure<__fn> {
139+
// [range.as.const.overview]: the basic `constant_range` case
140+
template <class _Range>
141+
requires constant_range<all_t<_Range>>
142+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
143+
operator()(_Range&& __range) noexcept(noexcept(views::all(std::forward<_Range>(__range))))
144+
-> decltype(/*--------------------------*/ views::all(std::forward<_Range>(__range))) {
145+
return /*---------------------------------*/ views::all(std::forward<_Range>(__range));
146+
}
147+
148+
// [range.as.const.overview]: the `empty_view` case
149+
template <class _Range, class _UType = std::remove_cvref_t<_Range>>
150+
requires(!constant_range<all_t<_Range>>) && __has_type<__empty_view_case<_UType>>
151+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
152+
operator()(_Range&&) noexcept(noexcept(auto(views::empty<typename __empty_view_case<_UType>::type>)))
153+
-> decltype(/*------------------*/ auto(views::empty<typename __empty_view_case<_UType>::type>)) {
154+
return /*-------------------------*/ auto(views::empty<typename __empty_view_case<_UType>::type>);
155+
}
156+
157+
// [range.as.const.overview]: the `span` case
158+
template <class _Range, class _UType = std::remove_cvref_t<_Range>>
159+
requires(!constant_range<all_t<_Range>>) && __has_type<__span_case<_UType>>
160+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
161+
operator()(_Range&& __range) noexcept(noexcept(typename __span_case<_UType>::type(std::forward<_UType>(__range))))
162+
-> decltype(/*--------------------------*/ typename __span_case<_UType>::type(std::forward<_UType>(__range))) {
163+
return /*---------------------------------*/ typename __span_case<_UType>::type(std::forward<_UType>(__range));
164+
}
165+
166+
// [range.as.const.overview]: the `ref_view` case
167+
template <class _Range, class _UType = std::remove_cvref_t<_Range>>
168+
requires(!constant_range<all_t<_Range>>) && __has_type<__ref_view_case<_UType>>
169+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range) noexcept(
170+
noexcept(ref_view(static_cast<typename __ref_view_case<_UType>::type>(__range.base()))))
171+
-> decltype(/*--------------------------*/ ref_view(
172+
static_cast<typename __ref_view_case<_UType>::type>(__range.base()))) {
173+
return /*---------------------------------*/ ref_view(
174+
static_cast<typename __ref_view_case<_UType>::type>(__range.base()));
175+
}
176+
177+
// [range.as.const.overview]: the second `constant_range` case
178+
template <class _Range, class _UType = std::remove_cvref_t<_Range>>
179+
requires(!constant_range<all_t<_Range>>) && is_lvalue_reference_v<_Range> &&
180+
__has_type<__constant_range_case<_UType>>
181+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range) noexcept(
182+
noexcept(ref_view(static_cast<typename __constant_range_case<_UType>::type>(__range))))
183+
-> decltype(/*--------------------------*/ ref_view(
184+
static_cast<typename __constant_range_case<_UType>::type>(__range))) {
185+
return /*---------------------------------*/ ref_view(
186+
static_cast<typename __constant_range_case<_UType>::type>(__range));
187+
}
188+
189+
// [range.as.const.overview]: otherwise
190+
template <class _Range>
191+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
192+
operator()(_Range&& __range) noexcept(noexcept(as_const_view(std::forward<_Range>(__range))))
193+
-> decltype(/*--------------------------*/ as_const_view(std::forward<_Range>(__range))) {
194+
return /*---------------------------------*/ as_const_view(std::forward<_Range>(__range));
195+
}
196+
};
197+
198+
} // namespace __as_const
199+
200+
inline namespace __cpo {
201+
inline constexpr auto as_const = __as_const::__fn{};
202+
} // namespace __cpo
203+
} // namespace views
204+
205+
} // namespace ranges
206+
207+
#endif // _LIBCPP_STD_VER >= 23
208+
209+
_LIBCPP_END_NAMESPACE_STD
210+
211+
_LIBCPP_POP_MACROS
212+
213+
#endif // _LIBCPP___RANGES_AS_CONST_VIEW_H

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,7 @@ module std_private_ranges_all [system] {
17001700
export std_private_functional_perfect_forward
17011701
export std_private_ranges_owning_view
17021702
}
1703+
module std_private_ranges_as_const_view [system] { header "__ranges/as_const_view.h" }
17031704
module std_private_ranges_as_rvalue_view [system] { header "__ranges/as_rvalue_view.h" }
17041705
module std_private_ranges_chunk_by_view [system] { header "__ranges/chunk_by_view.h" }
17051706
module std_private_ranges_common_view [system] { header "__ranges/common_view.h" }

libcxx/include/ranges

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ namespace std {
430430
#endif
431431

432432
#if _LIBCPP_STD_VER >= 23
433+
# include <__ranges/as_const_view.h>
433434
# include <__ranges/as_rvalue_view.h>
434435
# include <__ranges/chunk_by_view.h>
435436
# include <__ranges/from_range.h>
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
// std::views::as_const
12+
13+
#include <cassert>
14+
#include <functional>
15+
#include <ranges>
16+
#include <vector>
17+
18+
#include "test_iterators.h"
19+
20+
template <class View, class T>
21+
concept HasPipe = requires {
22+
{ std::declval<View>() | std::declval<T>() };
23+
};
24+
25+
struct DefaultConstructibleView : std::ranges::view_base {
26+
int i_;
27+
int* begin();
28+
int* end();
29+
};
30+
struct NoView {};
31+
32+
static_assert(std::is_invocable_v<decltype(std::views::as_const), DefaultConstructibleView>);
33+
static_assert(!std::is_invocable_v<decltype(std::views::as_const)>);
34+
static_assert(!std::is_invocable_v<decltype(std::views::as_const), NoView>);
35+
static_assert(HasPipe<DefaultConstructibleView&, decltype(std::views::as_const)>);
36+
static_assert(HasPipe<int (&)[10], decltype(std::views::as_const)>);
37+
static_assert(!HasPipe<int (&&)[10], decltype(std::views::as_const)>);
38+
static_assert(!HasPipe<NoView, decltype(std::views::as_const)>);
39+
static_assert(std::is_same_v<decltype(std::views::as_const), decltype(std::ranges::views::as_const)>);
40+
41+
struct const_iterator_range {
42+
constexpr std::const_iterator<int*> begin() const { return {}; }
43+
constexpr std::const_iterator<int*> end() const { return {}; }
44+
};
45+
static_assert(!std::ranges::view<const_iterator_range>);
46+
static_assert(std::ranges::range<const_iterator_range>);
47+
48+
constexpr bool test() {
49+
// Let E be an expression, let T be decltype((E)), and let U be remove_cvref_t<T>.
50+
// The expression views::as_const(E) is expression-equivalent to:
51+
52+
// - If views::all_t<T> models constant_range, then views::all(E).
53+
{
54+
[[maybe_unused]] std::same_as<std::views::all_t<const_iterator_range>> decltype(auto) view =
55+
const_iterator_range{} | std::views::as_const;
56+
}
57+
{
58+
// ambiguous with empty_view case
59+
[[maybe_unused]] std::same_as<std::views::all_t<std::ranges::empty_view<const int>>> decltype(auto) view =
60+
std::views::empty<const int> | std::views::as_const;
61+
}
62+
{
63+
// ambiguous with span case
64+
int a[3] = {};
65+
[[maybe_unused]] std::same_as<std::views::all_t<std::span<const int>>> decltype(auto) view1 =
66+
std::span<const int>(a) | std::views::as_const;
67+
[[maybe_unused]] std::same_as<std::views::all_t<std::span<const int, 3>>> decltype(auto) view2 =
68+
std::span<const int, 3>(a) | std::views::as_const;
69+
}
70+
{
71+
// ambiguous with ref_view case
72+
std::array<int, 3> a = {};
73+
std::ranges::ref_view<const std::array<int, 3>> r = a;
74+
[[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
75+
r | std::views::as_const;
76+
}
77+
{
78+
// ambiguous with constant_range case
79+
std::array<const int, 3> a = {};
80+
[[maybe_unused]] std::same_as<std::ranges::ref_view<std::array<const int, 3>>> decltype(auto) view =
81+
a | std::views::as_const;
82+
}
83+
84+
// - Otherwise, if U denotes empty_view<X> for some type X, then auto(views::empty<const X>).
85+
{
86+
[[maybe_unused]] std::same_as<std::ranges::empty_view<const int>> decltype(auto) view =
87+
std::views::empty<int> | std::views::as_const;
88+
}
89+
90+
// - Otherwise, if U denotes span<X, Extent> for some type X and some extent Extent, then span<const X, Extent>(E).
91+
{
92+
int a[3] = {};
93+
std::same_as<std::span<const int>> decltype(auto) view = std::span<int>(a) | std::views::as_const;
94+
assert(std::to_address(view.begin()) == a);
95+
assert(std::to_address(view.end()) == a + 3);
96+
}
97+
{
98+
int a[3] = {};
99+
std::same_as<std::span<const int, 3>> decltype(auto) view = std::span<int, 3>(a) | std::views::as_const;
100+
assert(std::to_address(view.begin()) == a);
101+
assert(std::to_address(view.end()) == a + 3);
102+
}
103+
104+
// - Otherwise, if U denotes ref_view<X> for some type X and const X models constant_range, then ref_view(static_cast<const X&>(E.base())).
105+
{
106+
std::array<int, 3> a = {};
107+
std::ranges::ref_view<std::array<int, 3>> r = a;
108+
[[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
109+
r | std::views::as_const;
110+
}
111+
112+
// - Otherwise, if E is an lvalue, const U models constant_range, and U does not model view, then ref_view(static_cast<const U&>(E)).
113+
{
114+
std::array<int, 3> a = {};
115+
[[maybe_unused]] std::same_as<std::ranges::ref_view<const std::array<int, 3>>> decltype(auto) view =
116+
a | std::views::as_const;
117+
}
118+
119+
// - Otherwise, as_const_view(E).
120+
{ // view | views::as_const
121+
DefaultConstructibleView v{{}, 3};
122+
std::same_as<std::ranges::as_const_view<DefaultConstructibleView>> decltype(auto) view = v | std::views::as_const;
123+
assert(view.base().i_ == 3);
124+
}
125+
126+
{ // adaptor | views::as_const
127+
DefaultConstructibleView v{{}, 3};
128+
const auto partial = std::views::transform(std::identity{}) | std::views::as_const;
129+
std::same_as<std::ranges::as_const_view<
130+
std::ranges::transform_view<DefaultConstructibleView, std::identity>>> decltype(auto) view = partial(v);
131+
assert(view.base().base().i_ == 3);
132+
}
133+
134+
{ // views::as_const | adaptor
135+
DefaultConstructibleView v{{}, 3};
136+
const auto partial = std::views::as_const | std::views::transform(std::identity{});
137+
std::same_as<std::ranges::transform_view<std::ranges::as_const_view<DefaultConstructibleView>,
138+
std::identity>> decltype(auto) view = partial(v);
139+
assert(view.base().base().i_ == 3);
140+
}
141+
142+
{ // range | views::as_const
143+
[[maybe_unused]] std::same_as<std::ranges::as_const_view<std::views::all_t<std::vector<int>>>> decltype(auto) view =
144+
std::vector<int>{} | std::views::as_const;
145+
}
146+
147+
return true;
148+
}
149+
150+
int main(int, char**) {
151+
test();
152+
static_assert(test());
153+
154+
return 0;
155+
}

0 commit comments

Comments
 (0)