216 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// Testing std::ranges::iota10 11// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2012 13#include <algorithm>14#include <array>15#include <cassert>16#include <numeric>17#include <utility>18 19#include "almost_satisfies_types.h"20#include "test_iterators.h"21#include "test_macros.h"22 23//24// Testing constraints25//26 27// Concepts to check different overloads of std::ranges::iota28template <class Iter = int*, class Sent = int*, class Value = int>29concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {30 std::ranges::iota(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Value>(val));31};32 33template <class Range, class Value = int>34concept HasIotaRange =35 requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };36 37// Test constraints of the iterator/sentinel overload38// ==================================================39static_assert(HasIotaIter<int*, int*, int>);40 41// !input_or_output_iterator<O>42static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);43 44// !sentinel_for<S, O>45static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);46static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);47 48// !weakly_incrementable<T>49static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);50 51// !indirectly writable <O, T>52static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);53 54// Test constraints for the range overload55// =======================================56static_assert(HasIotaRange<UncheckedRange<int*>, int>);57 58// !weakly_incrementable<T>59static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);60 61// !ranges::output_range<const _Tp&>62static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);63 64//65// Testing results66//67 68struct DangerousCopyAssign {69 int val;70 using difference_type = int;71 72 constexpr explicit DangerousCopyAssign(int v) : val(v) {}73 74 // Needed in postfix75 constexpr DangerousCopyAssign(DangerousCopyAssign const& other) { this->val = other.val; }76 77 /*78 This class has a "mischievous" non-const overload of copy-assignment79 operator that modifies the object being assigned from. `ranges::iota`80 should not be invoking this overload thanks to the `std::as_const` in its81 implementation. If for some reason it does invoke it, there will be a compiler82 error.83 */84 constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) = delete;85 86 // safe copy assignment std::as_const inside ranges::iota should ensure this87 // overload gets called88 constexpr DangerousCopyAssign& operator=(DangerousCopyAssign const& a) {89 this->val = a.val;90 return *this;91 }92 93 constexpr bool operator==(DangerousCopyAssign const& rhs) { return this->val == rhs.val; }94 95 // prefix96 constexpr DangerousCopyAssign& operator++() {97 ++(this->val);98 return *this;99 }100 101 // postfix102 constexpr DangerousCopyAssign operator++(int) {103 auto tmp = *this;104 ++this->val;105 return tmp;106 }107};108 109template <class Iter, class Sent, std::size_t N>110constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {111 { // (iterator, sentinel) overload112 auto in_begin = Iter(input.data());113 auto in_end = Sent(Iter(input.data() + input.size()));114 std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =115 std::ranges::iota(std::move(in_begin), std::move(in_end), starting_value);116 assert(result.out == in_end);117 assert(result.value == starting_value + static_cast<int>(N));118 assert(std::ranges::equal(input, expected));119 }120 121 { // (range) overload122 // in the range overload adds the additional constraint that it must be an output range123 // so skip this for the input iterators we test124 auto in_begin = Iter(input.data());125 auto in_end = Sent(Iter(input.data() + input.size()));126 auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end));127 128 std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =129 std::ranges::iota(range, starting_value);130 assert(result.out == in_end);131 assert(result.value == starting_value + static_cast<int>(N));132 assert(std::ranges::equal(input, expected));133 }134}135 136template <class Iter, class Sent = sentinel_wrapper<Iter>>137constexpr void test_results() {138 // Empty139 test_result<Iter, Sent, 0>({}, 0, {});140 // 1-element sequence141 test_result<Iter, Sent, 1>({1}, 0, {0});142 // Longer sequence143 test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});144}145 146constexpr void test_user_defined_type() {147 // Simple non-fundamental type148 struct UserDefinedType {149 int val;150 using difference_type = int;151 152 constexpr explicit UserDefinedType(int v) : val(v) {}153 constexpr UserDefinedType(UserDefinedType const& other) { this->val = other.val; }154 constexpr UserDefinedType& operator=(UserDefinedType const& a) {155 this->val = a.val;156 return *this;157 }158 159 // prefix160 constexpr UserDefinedType& operator++() {161 ++(this->val);162 return *this;163 }164 165 // postfix166 constexpr UserDefinedType operator++(int) {167 auto tmp = *this;168 ++this->val;169 return tmp;170 }171 };172 173 // Setup174 using A = UserDefinedType;175 std::array<UserDefinedType, 5> a = {A{0}, A{0}, A{0}, A{0}, A{0}};176 std::array<UserDefinedType, 5> expected = {A{0}, A{1}, A{2}, A{3}, A{4}};177 178 // Fill with values179 std::ranges::iota(a, A{0});180 auto proj_val = [](UserDefinedType const& el) { return el.val; };181 182 // Check183 assert(std::ranges::equal(a, expected, std::ranges::equal_to{}, proj_val, proj_val));184}185 186constexpr void test_dangerous_copy_assign() {187 using A = DangerousCopyAssign;188 189 // If the dangerous non-const copy assignment is called, the final values in190 // aa should increment by 2 rather than 1.191 std::array<A, 3> aa = {A{0}, A{0}, A{0}};192 std::array<A, 3> expected = {A{0}, A{1}, A{2}};193 std::ranges::iota(aa, A{0});194 auto proj_val = [](DangerousCopyAssign const& el) { return el.val; };195 assert(std::ranges::equal(aa, expected, std::ranges::equal_to{}, proj_val, proj_val));196}197 198constexpr bool test_results() {199 // Tests on fundamental types200 types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });201 test_results<cpp17_output_iterator<int*>>();202 test_results<cpp20_output_iterator<int*>>();203 test_results<int*, sized_sentinel<int*>>();204 205 // Tests on non-fundamental types206 test_user_defined_type();207 test_dangerous_copy_assign();208 return true;209}210 211int main(int, char**) {212 test_results();213 static_assert(test_results());214 return 0;215}216