Skip to content

Commit f280505

Browse files
committed
[libcxx] adds std::indirectly_readable_traits to <iterator>
Implements parts of: * P0896R4 The One Ranges Proposal * LWG3446 `indirectly_readable_traits` ambiguity for types with both `value_type` and `element_type` Depends on D99141. Differential Revision: https://reviews.llvm.org/D99461
1 parent 9c776c2 commit f280505

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

libcxx/docs/Cxx2aStatusIssuesStatus.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,4 @@
297297
"`3396 <https://wg21.link/LWG3396>`__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","",""
298298
"`3397 <https://wg21.link/LWG3397>`__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","",""
299299
"`3398 <https://wg21.link/LWG3398>`__","``tuple_element_t``\ is also wrong for ``const subrange``\ ","Prague","",""
300+
"`3446 <https://wg21.link/LWG3446>`__","``indirectly_readable_traits``\ ambiguity for types with both ``value_type``\ and ``element_type``\ ","November virtual meeting","|Complete|","13.0"

libcxx/include/iterator

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
1818
namespace std
1919
{
20-
template<class> struct incrementable_traits; // since C++20
20+
template<class> struct incrementable_traits; // since C++20
21+
template<class> struct indirectly_readable_traits; // since C++20
2122
2223
template<class Iterator>
2324
struct iterator_traits
@@ -472,6 +473,53 @@ struct incrementable_traits<_Tp> {
472473
};
473474

474475
// TODO(cjdb): add iter_difference_t once iterator_traits is cleaned up.
476+
477+
// [readable.traits]
478+
template<class> struct __cond_value_type {};
479+
480+
template<class _Tp>
481+
requires is_object_v<_Tp>
482+
struct __cond_value_type<_Tp> { using value_type = remove_cv_t<_Tp>; };
483+
484+
template<class _Tp>
485+
concept __has_member_value_type = requires { typename _Tp::value_type; };
486+
487+
template<class _Tp>
488+
concept __has_member_element_type = requires { typename _Tp::element_type; };
489+
490+
template<class> struct indirectly_readable_traits {};
491+
492+
template<class _Ip>
493+
requires is_array_v<_Ip>
494+
struct indirectly_readable_traits<_Ip> {
495+
using value_type = remove_cv_t<remove_extent_t<_Ip>>;
496+
};
497+
498+
template<class _Ip>
499+
struct indirectly_readable_traits<const _Ip> : indirectly_readable_traits<_Ip> {};
500+
501+
template<class _Tp>
502+
struct indirectly_readable_traits<_Tp*> : __cond_value_type<_Tp> {};
503+
504+
template<__has_member_value_type _Tp>
505+
struct indirectly_readable_traits<_Tp>
506+
: __cond_value_type<typename _Tp::value_type> {};
507+
508+
template<__has_member_element_type _Tp>
509+
struct indirectly_readable_traits<_Tp>
510+
: __cond_value_type<typename _Tp::element_type> {};
511+
512+
// Pre-emptively applies LWG3541
513+
template<__has_member_value_type _Tp>
514+
requires __has_member_element_type<_Tp>
515+
struct indirectly_readable_traits<_Tp> {};
516+
template<__has_member_value_type _Tp>
517+
requires __has_member_element_type<_Tp> &&
518+
same_as<remove_cv_t<typename _Tp::element_type>,
519+
remove_cv_t<typename _Tp::value_type>>
520+
struct indirectly_readable_traits<_Tp>
521+
: __cond_value_type<typename _Tp::value_type> {};
522+
475523
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
476524

477525
template <class _Iter>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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
10+
// UNSUPPORTED: libcpp-no-concepts
11+
12+
// template<class T>
13+
// struct indirectly_readable_traits;
14+
15+
#include <iterator>
16+
17+
#include <concepts>
18+
#include <memory>
19+
#include <optional>
20+
#include <string>
21+
#include <vector>
22+
23+
// `value_type` and `element_type` member aliases aren't actually used to declare anytihng, so GCC
24+
// thinks they're completely unused.
25+
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
26+
27+
// clang-format off
28+
template <class T>
29+
concept check_has_value_type = requires {
30+
typename std::indirectly_readable_traits<T>::value_type;
31+
};
32+
33+
template <class T, class Expected>
34+
concept check_value_type_matches =
35+
check_has_value_type<T> &&
36+
std::same_as<typename std::indirectly_readable_traits<T>::value_type, Expected>;
37+
// clang-format on
38+
39+
template <class T>
40+
constexpr bool check_pointer() {
41+
constexpr bool result = check_value_type_matches<T*, T>;
42+
static_assert(check_value_type_matches<T const*, T> == result);
43+
static_assert(check_value_type_matches<T volatile*, T> == result);
44+
static_assert(check_value_type_matches<T const volatile*, T> == result);
45+
46+
static_assert(check_value_type_matches<T* const, T> == result);
47+
static_assert(check_value_type_matches<T const* const, T> == result);
48+
static_assert(check_value_type_matches<T volatile* const, T> == result);
49+
static_assert(check_value_type_matches<T const volatile* const, T> == result);
50+
51+
return result;
52+
}
53+
54+
static_assert(!check_pointer<void>());
55+
static_assert(check_pointer<int>());
56+
static_assert(check_pointer<long>());
57+
static_assert(check_pointer<double>());
58+
static_assert(check_pointer<double*>());
59+
60+
struct S {};
61+
static_assert(check_pointer<S>());
62+
63+
template <class T>
64+
constexpr bool check_array() {
65+
constexpr bool result = check_value_type_matches<T[], T>;
66+
static_assert(check_value_type_matches<T const[], T> == result);
67+
static_assert(check_value_type_matches<T volatile[], T> == result);
68+
static_assert(check_value_type_matches<T const volatile[], T> == result);
69+
static_assert(check_value_type_matches<T[10], T> == result);
70+
static_assert(check_value_type_matches<T const[10], T> == result);
71+
static_assert(check_value_type_matches<T volatile[10], T> == result);
72+
static_assert(check_value_type_matches<T const volatile[10], T> == result);
73+
return result;
74+
}
75+
76+
static_assert(check_array<int>());
77+
static_assert(check_array<long>());
78+
static_assert(check_array<double>());
79+
static_assert(check_array<double*>());
80+
static_assert(check_array<S>());
81+
82+
template <class T, class Expected>
83+
constexpr bool check_explicit_member() {
84+
constexpr bool result = check_value_type_matches<T, Expected>;
85+
static_assert(check_value_type_matches<T const, Expected> == result);
86+
return result;
87+
}
88+
89+
struct has_value_type {
90+
using value_type = int;
91+
};
92+
static_assert(check_explicit_member<has_value_type, int>());
93+
static_assert(check_explicit_member<std::vector<int>::iterator, int>());
94+
95+
struct has_element_type {
96+
using element_type = S;
97+
};
98+
static_assert(check_explicit_member<has_element_type, S>());
99+
100+
struct has_same_value_and_element_type {
101+
using value_type = int;
102+
using element_type = int;
103+
};
104+
static_assert(check_explicit_member<has_same_value_and_element_type, int>());
105+
static_assert(check_explicit_member<std::shared_ptr<long>, long>());
106+
static_assert(check_explicit_member<std::shared_ptr<long const>, long>());
107+
108+
// clang-format off
109+
template<class T, class U>
110+
requires std::same_as<std::remove_cv_t<T>, std::remove_cv_t<U> >
111+
struct possibly_different_cv_qualifiers {
112+
using value_type = T;
113+
using element_type = U;
114+
};
115+
// clang-format on
116+
117+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int, int>, int>());
118+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int, int const>, int>());
119+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int, int volatile>, int>());
120+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int, int const volatile>, int>());
121+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const, int>, int>());
122+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const, int const>, int>());
123+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const, int volatile>, int>());
124+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const, int const volatile>, int>());
125+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int volatile, int>, int>());
126+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int volatile, int const>, int>());
127+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int volatile, int volatile>, int>());
128+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int volatile, int const volatile>, int>());
129+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const volatile, int>, int>());
130+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const volatile, int const>, int>());
131+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const volatile, int volatile>, int>());
132+
static_assert(check_explicit_member<possibly_different_cv_qualifiers<int const volatile, int const volatile>, int>());
133+
134+
struct S2 {};
135+
namespace std {
136+
template <>
137+
struct indirectly_readable_traits<S2> {
138+
using value_type = int;
139+
};
140+
} // namespace std
141+
static_assert(check_value_type_matches<S2, int>);
142+
static_assert(check_value_type_matches<std::vector<int>, int>);
143+
static_assert(check_value_type_matches<std::vector<int>::iterator, int>);
144+
static_assert(check_value_type_matches<std::vector<int>::const_iterator, int>);
145+
static_assert(check_value_type_matches<std::istream_iterator<int>, int>);
146+
static_assert(check_value_type_matches<std::istreambuf_iterator<char>, char>);
147+
static_assert(check_value_type_matches<std::optional<int>, int>);
148+
149+
template <class T>
150+
constexpr bool check_ref() {
151+
struct ref_value {
152+
using value_type = T&;
153+
};
154+
constexpr bool result = check_has_value_type<ref_value>;
155+
156+
struct ref_element {
157+
using element_type = T&;
158+
};
159+
static_assert(check_has_value_type<ref_element> == result);
160+
161+
return result;
162+
}
163+
164+
static_assert(!check_ref<int>());
165+
static_assert(!check_ref<S>());
166+
static_assert(!check_ref<std::shared_ptr<long> >());
167+
168+
static_assert(!check_has_value_type<void>);
169+
static_assert(!check_has_value_type<std::nullptr_t>);
170+
static_assert(!check_has_value_type<int>);
171+
static_assert(!check_has_value_type<S>);
172+
173+
struct has_different_value_and_element_type {
174+
using value_type = int;
175+
using element_type = long;
176+
};
177+
static_assert(!check_has_value_type<has_different_value_and_element_type>);
178+
179+
struct void_value {
180+
using value_type = void;
181+
};
182+
static_assert(!check_has_value_type<void_value>);
183+
184+
struct void_element {
185+
using element_type = void;
186+
};
187+
static_assert(!check_has_value_type<void_element>);

0 commit comments

Comments
 (0)