| 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 | |
| 11 | // template<class D> |
| 12 | // requires is_class_v<D> && same_as<D, remove_cv_t<D>> |
| 13 | // class view_interface; |
| 14 | |
| 15 | #include <ranges> |
| 16 | |
| 17 | #include <cassert> |
| 18 | #include <utility> |
| 19 | #include "test_macros.h" |
| 20 | #include "test_iterators.h" |
| 21 | |
| 22 | template<class T> |
| 23 | concept ValidViewInterfaceType = requires { typename std::ranges::view_interface<T>; }; |
| 24 | |
| 25 | struct Empty { }; |
| 26 | |
| 27 | static_assert(!ValidViewInterfaceType<void>); |
| 28 | static_assert(!ValidViewInterfaceType<void*>); |
| 29 | static_assert(!ValidViewInterfaceType<Empty*>); |
| 30 | static_assert(!ValidViewInterfaceType<Empty const>); |
| 31 | static_assert(!ValidViewInterfaceType<Empty &>); |
| 32 | static_assert( ValidViewInterfaceType<Empty>); |
| 33 | |
| 34 | using InputIter = cpp20_input_iterator<const int*>; |
| 35 | |
| 36 | struct InputRange : std::ranges::view_interface<InputRange> { |
| 37 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 38 | constexpr InputIter begin() const { return InputIter(buff); } |
| 39 | constexpr InputIter end() const { return InputIter(buff + 8); } |
| 40 | }; |
| 41 | |
| 42 | struct SizedInputRange : std::ranges::view_interface<SizedInputRange> { |
| 43 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 44 | constexpr InputIter begin() const { return InputIter(buff); } |
| 45 | constexpr sentinel_wrapper<InputIter> end() const { return sentinel_wrapper(InputIter(buff + 8)); } |
| 46 | constexpr std::size_t size() const { return 8; } |
| 47 | }; |
| 48 | static_assert(std::ranges::sized_range<SizedInputRange>); |
| 49 | |
| 50 | struct NotSizedSentinel { |
| 51 | using value_type = int; |
| 52 | using difference_type = std::ptrdiff_t; |
| 53 | using iterator_concept = std::forward_iterator_tag; |
| 54 | |
| 55 | explicit NotSizedSentinel() = default; |
| 56 | explicit NotSizedSentinel(int*); |
| 57 | int& operator*() const; |
| 58 | NotSizedSentinel& operator++(); |
| 59 | NotSizedSentinel operator++(int); |
| 60 | bool operator==(NotSizedSentinel const&) const; |
| 61 | }; |
| 62 | static_assert(std::forward_iterator<NotSizedSentinel>); |
| 63 | |
| 64 | using ForwardIter = forward_iterator<int*>; |
| 65 | |
| 66 | // So that we conform to sized_sentinel_for. |
| 67 | constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { |
| 68 | return base(x) - base(y); |
| 69 | } |
| 70 | |
| 71 | struct ForwardRange : std::ranges::view_interface<ForwardRange> { |
| 72 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 73 | constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); } |
| 74 | constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); } |
| 75 | }; |
| 76 | static_assert(std::ranges::view<ForwardRange>); |
| 77 | |
| 78 | struct MoveOnlyForwardRange : std::ranges::view_interface<MoveOnlyForwardRange> { |
| 79 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 80 | MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete; |
| 81 | MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default; |
| 82 | MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default; |
| 83 | MoveOnlyForwardRange() = default; |
| 84 | constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); } |
| 85 | constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); } |
| 86 | }; |
| 87 | static_assert(std::ranges::view<MoveOnlyForwardRange>); |
| 88 | |
| 89 | struct MI : std::ranges::view_interface<InputRange>, |
| 90 | std::ranges::view_interface<MoveOnlyForwardRange> { |
| 91 | }; |
| 92 | static_assert(!std::ranges::view<MI>); |
| 93 | |
| 94 | struct EmptyIsTrue : std::ranges::view_interface<EmptyIsTrue> { |
| 95 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 96 | constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); } |
| 97 | constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); } |
| 98 | constexpr bool empty() const { return true; } |
| 99 | }; |
| 100 | static_assert(std::ranges::view<EmptyIsTrue>); |
| 101 | |
| 102 | struct SizeIsTen : std::ranges::view_interface<SizeIsTen> { |
| 103 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 104 | constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); } |
| 105 | constexpr ForwardIter end() const { return ForwardIter(const_cast<int*>(buff) + 8); } |
| 106 | constexpr std::size_t size() const { return 10; } |
| 107 | }; |
| 108 | static_assert(std::ranges::view<SizeIsTen>); |
| 109 | |
| 110 | using RAIter = random_access_iterator<int*>; |
| 111 | |
| 112 | struct RARange : std::ranges::view_interface<RARange> { |
| 113 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 114 | constexpr RAIter begin() const { return RAIter(const_cast<int*>(buff)); } |
| 115 | constexpr RAIter end() const { return RAIter(const_cast<int*>(buff) + 8); } |
| 116 | }; |
| 117 | static_assert(std::ranges::view<RARange>); |
| 118 | |
| 119 | using ContIter = contiguous_iterator<const int*>; |
| 120 | |
| 121 | struct ContRange : std::ranges::view_interface<ContRange> { |
| 122 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 123 | constexpr ContIter begin() const { return ContIter(buff); } |
| 124 | constexpr ContIter end() const { return ContIter(buff + 8); } |
| 125 | }; |
| 126 | static_assert(std::ranges::view<ContRange>); |
| 127 | |
| 128 | struct DataIsNull : std::ranges::view_interface<DataIsNull> { |
| 129 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 130 | constexpr ContIter begin() const { return ContIter(buff); } |
| 131 | constexpr ContIter end() const { return ContIter(buff + 8); } |
| 132 | constexpr const int *data() const { return nullptr; } |
| 133 | }; |
| 134 | static_assert(std::ranges::view<DataIsNull>); |
| 135 | |
| 136 | struct BoolConvertibleComparison : std::ranges::view_interface<BoolConvertibleComparison> { |
| 137 | struct ResultType { |
| 138 | bool value; |
| 139 | constexpr operator bool() const { return value; } |
| 140 | }; |
| 141 | |
| 142 | struct SentinelType { |
| 143 | int *base_; |
| 144 | explicit SentinelType() = default; |
| 145 | constexpr explicit SentinelType(int *base) : base_(base) {} |
| 146 | friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; } |
| 147 | friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; } |
| 148 | friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; } |
| 149 | friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; } |
| 150 | }; |
| 151 | |
| 152 | int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
| 153 | constexpr ForwardIter begin() const { return ForwardIter(const_cast<int*>(buff)); } |
| 154 | constexpr SentinelType end() const { return SentinelType(const_cast<int*>(buff) + 8); } |
| 155 | }; |
| 156 | static_assert(std::ranges::view<BoolConvertibleComparison>); |
| 157 | |
| 158 | template<class T> |
| 159 | concept EmptyInvocable = requires (T const& obj) { obj.empty(); }; |
| 160 | |
| 161 | template<class T> |
| 162 | concept BoolOpInvocable = requires (T const& obj) { bool(obj); }; |
| 163 | |
| 164 | constexpr bool testEmpty() { |
| 165 | static_assert(!EmptyInvocable<InputRange>); |
| 166 | // LWG 3715: `view_interface::empty` is overconstrained |
| 167 | static_assert(EmptyInvocable<SizedInputRange>); |
| 168 | static_assert( EmptyInvocable<ForwardRange>); |
| 169 | |
| 170 | static_assert(!BoolOpInvocable<InputRange>); |
| 171 | static_assert(BoolOpInvocable<SizedInputRange>); |
| 172 | static_assert( BoolOpInvocable<ForwardRange>); |
| 173 | |
| 174 | SizedInputRange sizedInputRange; |
| 175 | assert(!sizedInputRange.empty()); |
| 176 | assert(!static_cast<SizedInputRange const&>(sizedInputRange).empty()); |
| 177 | |
| 178 | assert(sizedInputRange); |
| 179 | assert(static_cast<SizedInputRange const&>(sizedInputRange)); |
| 180 | |
| 181 | assert(!std::ranges::empty(sizedInputRange)); |
| 182 | assert(!std::ranges::empty(static_cast<SizedInputRange const&>(sizedInputRange))); |
| 183 | |
| 184 | ForwardRange forwardRange; |
| 185 | assert(!forwardRange.empty()); |
| 186 | assert(!static_cast<ForwardRange const&>(forwardRange).empty()); |
| 187 | |
| 188 | assert(forwardRange); |
| 189 | assert(static_cast<ForwardRange const&>(forwardRange)); |
| 190 | |
| 191 | assert(!std::ranges::empty(forwardRange)); |
| 192 | assert(!std::ranges::empty(static_cast<ForwardRange const&>(forwardRange))); |
| 193 | |
| 194 | EmptyIsTrue emptyTrue; |
| 195 | assert(emptyTrue.empty()); |
| 196 | assert(static_cast<EmptyIsTrue const&>(emptyTrue).empty()); |
| 197 | assert(!emptyTrue.std::ranges::view_interface<EmptyIsTrue>::empty()); |
| 198 | |
| 199 | assert(!emptyTrue); |
| 200 | assert(!static_cast<EmptyIsTrue const&>(emptyTrue)); |
| 201 | assert(!emptyTrue.std::ranges::view_interface<EmptyIsTrue>::operator bool()); |
| 202 | |
| 203 | assert(std::ranges::empty(emptyTrue)); |
| 204 | assert(std::ranges::empty(static_cast<EmptyIsTrue const&>(emptyTrue))); |
| 205 | |
| 206 | // Try calling empty on an rvalue. |
| 207 | MoveOnlyForwardRange moveOnly; |
| 208 | assert(!std::move(moveOnly).empty()); |
| 209 | |
| 210 | BoolConvertibleComparison boolConv; |
| 211 | ASSERT_NOT_NOEXCEPT(boolConv.empty()); |
| 212 | |
| 213 | assert(!boolConv.empty()); |
| 214 | assert(!static_cast<const BoolConvertibleComparison&>(boolConv).empty()); |
| 215 | |
| 216 | assert(boolConv); |
| 217 | assert(static_cast<const BoolConvertibleComparison&>(boolConv)); |
| 218 | |
| 219 | assert(!std::ranges::empty(boolConv)); |
| 220 | assert(!std::ranges::empty(static_cast<const BoolConvertibleComparison&>(boolConv))); |
| 221 | |
| 222 | return true; |
| 223 | } |
| 224 | |
| 225 | template<class T> |
| 226 | concept DataInvocable = requires (T const& obj) { obj.data(); }; |
| 227 | |
| 228 | constexpr bool testData() { |
| 229 | static_assert(!DataInvocable<ForwardRange>); |
| 230 | static_assert( DataInvocable<ContRange>); |
| 231 | |
| 232 | ContRange contiguous; |
| 233 | assert(contiguous.data() == contiguous.buff); |
| 234 | assert(static_cast<ContRange const&>(contiguous).data() == contiguous.buff); |
| 235 | |
| 236 | assert(std::ranges::data(contiguous) == contiguous.buff); |
| 237 | assert(std::ranges::data(static_cast<ContRange const&>(contiguous)) == contiguous.buff); |
| 238 | |
| 239 | DataIsNull dataNull; |
| 240 | assert(dataNull.data() == nullptr); |
| 241 | assert(static_cast<DataIsNull const&>(dataNull).data() == nullptr); |
| 242 | assert(dataNull.std::ranges::view_interface<DataIsNull>::data() == dataNull.buff); |
| 243 | |
| 244 | assert(std::ranges::data(dataNull) == nullptr); |
| 245 | assert(std::ranges::data(static_cast<DataIsNull const&>(dataNull)) == nullptr); |
| 246 | |
| 247 | return true; |
| 248 | } |
| 249 | |
| 250 | template<class T> |
| 251 | concept SizeInvocable = requires (T const& obj) { obj.size(); }; |
| 252 | |
| 253 | constexpr bool testSize() { |
| 254 | static_assert(!SizeInvocable<InputRange>); |
| 255 | static_assert(!SizeInvocable<NotSizedSentinel>); |
| 256 | static_assert( SizeInvocable<ForwardRange>); |
| 257 | |
| 258 | // Test the test. |
| 259 | static_assert(std::same_as<decltype(std::declval<ForwardIter>() - std::declval<ForwardIter>()), std::ptrdiff_t>); |
| 260 | using UnsignedSize = std::make_unsigned_t<std::ptrdiff_t>; |
| 261 | using SignedSize = std::common_type_t<std::ptrdiff_t, std::make_signed_t<UnsignedSize>>; |
| 262 | ForwardRange forwardRange; |
| 263 | assert(forwardRange.size() == 8); |
| 264 | assert(static_cast<ForwardRange const&>(forwardRange).size() == 8); |
| 265 | |
| 266 | assert(std::ranges::size(forwardRange) == 8); |
| 267 | static_assert(std::same_as<decltype(std::ranges::size(std::declval<ForwardRange>())), UnsignedSize>); |
| 268 | static_assert(std::same_as<decltype(std::ranges::ssize(std::declval<ForwardRange>())), SignedSize>); |
| 269 | |
| 270 | assert(std::ranges::size(static_cast<ForwardRange const&>(forwardRange)) == 8); |
| 271 | static_assert(std::same_as<decltype(std::ranges::size(std::declval<ForwardRange const>())), UnsignedSize>); |
| 272 | static_assert(std::same_as<decltype(std::ranges::ssize(std::declval<ForwardRange const>())), SignedSize>); |
| 273 | |
| 274 | SizeIsTen sizeTen; |
| 275 | assert(sizeTen.size() == 10); |
| 276 | assert(static_cast<SizeIsTen const&>(sizeTen).size() == 10); |
| 277 | assert(sizeTen.std::ranges::view_interface<SizeIsTen>::size() == 8); |
| 278 | |
| 279 | assert(std::ranges::size(sizeTen) == 10); |
| 280 | assert(std::ranges::size(static_cast<SizeIsTen const&>(sizeTen)) == 10); |
| 281 | |
| 282 | return true; |
| 283 | } |
| 284 | |
| 285 | template<class T> |
| 286 | concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; }; |
| 287 | |
| 288 | constexpr bool testSubscript() { |
| 289 | static_assert(!SubscriptInvocable<ForwardRange>); |
| 290 | static_assert( SubscriptInvocable<RARange>); |
| 291 | |
| 292 | RARange randomAccess; |
| 293 | assert(randomAccess[2] == 2); |
| 294 | assert(static_cast<RARange const&>(randomAccess)[2] == 2); |
| 295 | randomAccess[2] = 3; |
| 296 | assert(randomAccess[2] == 3); |
| 297 | |
| 298 | return true; |
| 299 | } |
| 300 | |
| 301 | template<class T> |
| 302 | concept FrontInvocable = requires (T const& obj) { obj.front(); }; |
| 303 | |
| 304 | template<class T> |
| 305 | concept BackInvocable = requires (T const& obj) { obj.back(); }; |
| 306 | |
| 307 | constexpr bool testFrontBack() { |
| 308 | static_assert(!FrontInvocable<InputRange>); |
| 309 | static_assert( FrontInvocable<ForwardRange>); |
| 310 | static_assert(!BackInvocable<ForwardRange>); |
| 311 | static_assert( BackInvocable<RARange>); |
| 312 | |
| 313 | ForwardRange forwardRange; |
| 314 | assert(forwardRange.front() == 0); |
| 315 | assert(static_cast<ForwardRange const&>(forwardRange).front() == 0); |
| 316 | forwardRange.front() = 2; |
| 317 | assert(forwardRange.front() == 2); |
| 318 | |
| 319 | RARange randomAccess; |
| 320 | assert(randomAccess.front() == 0); |
| 321 | assert(static_cast<RARange const&>(randomAccess).front() == 0); |
| 322 | randomAccess.front() = 2; |
| 323 | assert(randomAccess.front() == 2); |
| 324 | |
| 325 | assert(randomAccess.back() == 7); |
| 326 | assert(static_cast<RARange const&>(randomAccess).back() == 7); |
| 327 | randomAccess.back() = 2; |
| 328 | assert(randomAccess.back() == 2); |
| 329 | |
| 330 | return true; |
| 331 | } |
| 332 | |
| 333 | struct V1 : std::ranges::view_interface<V1> { }; |
| 334 | struct V2 : std::ranges::view_interface<V2> { V1 base_; }; |
| 335 | static_assert(sizeof(V2) == sizeof(V1)); |
| 336 | |
| 337 | int main(int, char**) { |
| 338 | testEmpty(); |
| 339 | static_assert(testEmpty()); |
| 340 | |
| 341 | testData(); |
| 342 | static_assert(testData()); |
| 343 | |
| 344 | testSize(); |
| 345 | static_assert(testSize()); |
| 346 | |
| 347 | testSubscript(); |
| 348 | static_assert(testSubscript()); |
| 349 | |
| 350 | testFrontBack(); |
| 351 | static_assert(testFrontBack()); |
| 352 | |
| 353 | return 0; |
| 354 | } |
| 355 | |