//===----------------------------------------------------------------------===// // // 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 // template // requires is_class_v && same_as> // class view_interface; #include #include #include #include "test_macros.h" #include "test_iterators.h" template concept ValidViewInterfaceType = requires { typename std::ranges::view_interface; }; struct Empty { }; static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert(!ValidViewInterfaceType); static_assert( ValidViewInterfaceType); using InputIter = cpp20_input_iterator; struct InputRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr InputIter begin() const { return InputIter(buff); } constexpr InputIter end() const { return InputIter(buff + 8); } }; struct SizedInputRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr InputIter begin() const { return InputIter(buff); } constexpr sentinel_wrapper end() const { return sentinel_wrapper(InputIter(buff + 8)); } constexpr std::size_t size() const { return 8; } }; static_assert(std::ranges::sized_range); struct NotSizedSentinel { using value_type = int; using difference_type = std::ptrdiff_t; using iterator_concept = std::forward_iterator_tag; explicit NotSizedSentinel() = default; explicit NotSizedSentinel(int*); int& operator*() const; NotSizedSentinel& operator++(); NotSizedSentinel operator++(int); bool operator==(NotSizedSentinel const&) const; }; static_assert(std::forward_iterator); using ForwardIter = forward_iterator; // So that we conform to sized_sentinel_for. constexpr std::ptrdiff_t operator-(const ForwardIter& x, const ForwardIter& y) { return base(x) - base(y); } struct ForwardRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } }; static_assert(std::ranges::view); struct MoveOnlyForwardRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; MoveOnlyForwardRange(MoveOnlyForwardRange const&) = delete; MoveOnlyForwardRange(MoveOnlyForwardRange &&) = default; MoveOnlyForwardRange& operator=(MoveOnlyForwardRange &&) = default; MoveOnlyForwardRange() = default; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } }; static_assert(std::ranges::view); struct MI : std::ranges::view_interface, std::ranges::view_interface { }; static_assert(!std::ranges::view); struct EmptyIsTrue : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } constexpr bool empty() const { return true; } }; static_assert(std::ranges::view); struct SizeIsTen : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } constexpr std::size_t size() const { return 10; } }; static_assert(std::ranges::view); using RAIter = random_access_iterator; struct RARange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr RAIter begin() const { return RAIter(const_cast(buff)); } constexpr RAIter end() const { return RAIter(const_cast(buff) + 8); } }; static_assert(std::ranges::view); using ContIter = contiguous_iterator; struct ContRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ContIter begin() const { return ContIter(buff); } constexpr ContIter end() const { return ContIter(buff + 8); } }; static_assert(std::ranges::view); struct DataIsNull : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ContIter begin() const { return ContIter(buff); } constexpr ContIter end() const { return ContIter(buff + 8); } constexpr const int *data() const { return nullptr; } }; static_assert(std::ranges::view); struct BoolConvertibleComparison : std::ranges::view_interface { struct ResultType { bool value; constexpr operator bool() const { return value; } }; struct SentinelType { int *base_; explicit SentinelType() = default; constexpr explicit SentinelType(int *base) : base_(base) {} friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) == sent.base_}; } friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) == sent.base_}; } friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {base(iter) != sent.base_}; } friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {base(iter) != sent.base_}; } }; int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr SentinelType end() const { return SentinelType(const_cast(buff) + 8); } }; static_assert(std::ranges::view); template concept EmptyInvocable = requires (T const& obj) { obj.empty(); }; template concept BoolOpInvocable = requires (T const& obj) { bool(obj); }; constexpr bool testEmpty() { static_assert(!EmptyInvocable); // LWG 3715: `view_interface::empty` is overconstrained static_assert(EmptyInvocable); static_assert( EmptyInvocable); static_assert(!BoolOpInvocable); static_assert(BoolOpInvocable); static_assert( BoolOpInvocable); SizedInputRange sizedInputRange; assert(!sizedInputRange.empty()); assert(!static_cast(sizedInputRange).empty()); assert(sizedInputRange); assert(static_cast(sizedInputRange)); assert(!std::ranges::empty(sizedInputRange)); assert(!std::ranges::empty(static_cast(sizedInputRange))); ForwardRange forwardRange; assert(!forwardRange.empty()); assert(!static_cast(forwardRange).empty()); assert(forwardRange); assert(static_cast(forwardRange)); assert(!std::ranges::empty(forwardRange)); assert(!std::ranges::empty(static_cast(forwardRange))); EmptyIsTrue emptyTrue; assert(emptyTrue.empty()); assert(static_cast(emptyTrue).empty()); assert(!emptyTrue.std::ranges::view_interface::empty()); assert(!emptyTrue); assert(!static_cast(emptyTrue)); assert(!emptyTrue.std::ranges::view_interface::operator bool()); assert(std::ranges::empty(emptyTrue)); assert(std::ranges::empty(static_cast(emptyTrue))); // Try calling empty on an rvalue. MoveOnlyForwardRange moveOnly; assert(!std::move(moveOnly).empty()); BoolConvertibleComparison boolConv; ASSERT_NOT_NOEXCEPT(boolConv.empty()); assert(!boolConv.empty()); assert(!static_cast(boolConv).empty()); assert(boolConv); assert(static_cast(boolConv)); assert(!std::ranges::empty(boolConv)); assert(!std::ranges::empty(static_cast(boolConv))); return true; } template concept DataInvocable = requires (T const& obj) { obj.data(); }; constexpr bool testData() { static_assert(!DataInvocable); static_assert( DataInvocable); ContRange contiguous; assert(contiguous.data() == contiguous.buff); assert(static_cast(contiguous).data() == contiguous.buff); assert(std::ranges::data(contiguous) == contiguous.buff); assert(std::ranges::data(static_cast(contiguous)) == contiguous.buff); DataIsNull dataNull; assert(dataNull.data() == nullptr); assert(static_cast(dataNull).data() == nullptr); assert(dataNull.std::ranges::view_interface::data() == dataNull.buff); assert(std::ranges::data(dataNull) == nullptr); assert(std::ranges::data(static_cast(dataNull)) == nullptr); return true; } template concept SizeInvocable = requires (T const& obj) { obj.size(); }; constexpr bool testSize() { static_assert(!SizeInvocable); static_assert(!SizeInvocable); static_assert( SizeInvocable); // Test the test. static_assert(std::same_as() - std::declval()), std::ptrdiff_t>); using UnsignedSize = std::make_unsigned_t; using SignedSize = std::common_type_t>; ForwardRange forwardRange; assert(forwardRange.size() == 8); assert(static_cast(forwardRange).size() == 8); assert(std::ranges::size(forwardRange) == 8); static_assert(std::same_as())), UnsignedSize>); static_assert(std::same_as())), SignedSize>); assert(std::ranges::size(static_cast(forwardRange)) == 8); static_assert(std::same_as())), UnsignedSize>); static_assert(std::same_as())), SignedSize>); SizeIsTen sizeTen; assert(sizeTen.size() == 10); assert(static_cast(sizeTen).size() == 10); assert(sizeTen.std::ranges::view_interface::size() == 8); assert(std::ranges::size(sizeTen) == 10); assert(std::ranges::size(static_cast(sizeTen)) == 10); return true; } template concept SubscriptInvocable = requires (T const& obj, std::size_t n) { obj[n]; }; constexpr bool testSubscript() { static_assert(!SubscriptInvocable); static_assert( SubscriptInvocable); RARange randomAccess; assert(randomAccess[2] == 2); assert(static_cast(randomAccess)[2] == 2); randomAccess[2] = 3; assert(randomAccess[2] == 3); return true; } template concept FrontInvocable = requires (T const& obj) { obj.front(); }; template concept BackInvocable = requires (T const& obj) { obj.back(); }; constexpr bool testFrontBack() { static_assert(!FrontInvocable); static_assert( FrontInvocable); static_assert(!BackInvocable); static_assert( BackInvocable); ForwardRange forwardRange; assert(forwardRange.front() == 0); assert(static_cast(forwardRange).front() == 0); forwardRange.front() = 2; assert(forwardRange.front() == 2); RARange randomAccess; assert(randomAccess.front() == 0); assert(static_cast(randomAccess).front() == 0); randomAccess.front() = 2; assert(randomAccess.front() == 2); assert(randomAccess.back() == 7); assert(static_cast(randomAccess).back() == 7); randomAccess.back() = 2; assert(randomAccess.back() == 2); return true; } struct V1 : std::ranges::view_interface { }; struct V2 : std::ranges::view_interface { V1 base_; }; static_assert(sizeof(V2) == sizeof(V1)); int main(int, char**) { testEmpty(); static_assert(testEmpty()); testData(); static_assert(testData()); testSize(); static_assert(testSize()); testSubscript(); static_assert(testSubscript()); testFrontBack(); static_assert(testFrontBack()); return 0; }