//===----------------------------------------------------------------------===// // // 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 // template // requires is_class_v && same_as> // class view_interface; #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); static_assert(std::derived_from, std::ranges::view_base>); 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 NotSizedSentinel { using I = int*; using value_type = std::iter_value_t; using difference_type = std::iter_difference_t; using iterator_concept = std::forward_iterator_tag; NotSizedSentinel() = default; explicit constexpr NotSizedSentinel(I); constexpr int &operator*() const { return *value; }; NotSizedSentinel& operator++(); NotSizedSentinel operator++(int); bool operator==(NotSizedSentinel const&) const; int *value; }; 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 x.base() - y.base(); } 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); } }; 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() = default; constexpr ForwardIter begin() const { return ForwardIter(const_cast(buff)); } constexpr ForwardIter end() const { return ForwardIter(const_cast(buff) + 8); } }; 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; } }; 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 size_t size() const { return 10; } }; 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); } }; 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); } }; 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; } }; template struct BoolConvertibleComparison : std::ranges::view_interface> { struct ResultType { bool value; constexpr operator bool() const noexcept(IsNoexcept) { return value; } }; struct SentinelType { int *base; SentinelType() = default; explicit constexpr SentinelType(int *base) : base(base) {} friend constexpr ResultType operator==(ForwardIter const& iter, SentinelType const& sent) noexcept { return {iter.base() == sent.base}; } friend constexpr ResultType operator==(SentinelType const& sent, ForwardIter const& iter) noexcept { return {iter.base() == sent.base}; } friend constexpr ResultType operator!=(ForwardIter const& iter, SentinelType const& sent) noexcept { return {iter.base() != sent.base}; } friend constexpr ResultType operator!=(SentinelType const& sent, ForwardIter const& iter) noexcept { return {iter.base() != sent.base}; } }; int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; constexpr ForwardIter begin() const noexcept { return ForwardIter(const_cast(buff)); } constexpr SentinelType end() const noexcept { return SentinelType(const_cast(buff) + 8); } }; template concept EmptyInvocable = requires (T const& obj) { obj.empty(); }; template concept BoolOpInvocable = requires (T const& obj) { bool(obj); }; constexpr bool testEmpty() { static_assert(!EmptyInvocable); static_assert( EmptyInvocable); static_assert(!BoolOpInvocable); static_assert( BoolOpInvocable); 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; BoolConvertibleComparison boolConv2; static_assert(noexcept(boolConv.empty())); static_assert(!noexcept(boolConv2.empty())); assert(!boolConv.empty()); assert(!static_cast const&>(boolConv).empty()); assert(boolConv); assert(static_cast const&>(boolConv)); assert(!std::ranges::empty(boolConv)); assert(!std::ranges::empty(static_cast const&>(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); ForwardRange forwardRange; assert(forwardRange.size() == 8); assert(static_cast(forwardRange).size() == 8); assert(std::ranges::size(forwardRange) == 8); assert(std::ranges::size(static_cast(forwardRange)) == 8); 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, 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; } 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; }