brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.9 KiB · 9fa50f1 Raw
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