250 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// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// <algorithm>12 13// template<input_iterator I, sentinel_for<I> S, weakly_incrementable O,14// copy_constructible F, class Proj = identity>15// requires indirectly_writable<O, indirect_result_t<F&, projected<I, Proj>>>16// constexpr ranges::unary_transform_result<I, O>17// ranges::transform(I first1, S last1, O result, F op, Proj proj = {});18// template<input_range R, weakly_incrementable O, copy_constructible F,19// class Proj = identity>20// requires indirectly_writable<O, indirect_result_t<F&, projected<iterator_t<R>, Proj>>>21// constexpr ranges::unary_transform_result<borrowed_iterator_t<R>, O>22// ranges::transform(R&& r, O result, F op, Proj proj = {});23 24#include <algorithm>25#include <array>26#include <cassert>27#include <functional>28#include <ranges>29 30#include "test_iterators.h"31#include "almost_satisfies_types.h"32 33template <class Range>34concept HasTransformR = requires(Range r, int* out) { std::ranges::transform(r, out, std::identity{}); };35 36static_assert(HasTransformR<std::array<int, 1>>);37static_assert(!HasTransformR<int>);38static_assert(!HasTransformR<InputRangeNotDerivedFrom>);39static_assert(!HasTransformR<InputRangeNotIndirectlyReadable>);40static_assert(!HasTransformR<InputRangeNotInputOrOutputIterator>);41static_assert(!HasTransformR<InputRangeNotSentinelSemiregular>);42static_assert(!HasTransformR<InputRangeNotSentinelEqualityComparableWith>);43 44template <class It, class Sent = It>45concept HasTransformIt =46 requires(It it, Sent sent, int* out) { std::ranges::transform(it, sent, out, std::identity{}); };47 48static_assert(HasTransformIt<int*>);49static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);50static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);51static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);52static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);53static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);54 55template <class It>56concept HasTransformOut = requires(int* it, int* sent, It out, std::array<int, 2> range) {57 std::ranges::transform(it, sent, out, std::identity{});58 std::ranges::transform(range, out, std::identity{});59};60static_assert(HasTransformOut<int*>);61static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);62 63// check indirectly_readable64static_assert(HasTransformOut<char*>);65static_assert(!HasTransformOut<int**>);66 67struct MoveOnlyFunctor {68 MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;69 MoveOnlyFunctor(MoveOnlyFunctor&&) = default;70 int operator()(int);71};72 73template <class Func>74concept HasTransformFuncUnary = requires(int* it, int* sent, int* out, std::array<int, 2> range, Func func) {75 std::ranges::transform(it, sent, out, func);76 std::ranges::transform(range, out, func);77};78static_assert(HasTransformFuncUnary<std::identity>);79static_assert(!HasTransformFuncUnary<MoveOnlyFunctor>);80 81static_assert(std::is_same_v<std::ranges::unary_transform_result<int, long>, std::ranges::in_out_result<int, long>>);82 83// clang-format off84template <class In1, class Out, class Sent1>85constexpr bool test_iterators() {86 { // simple87 {88 int a[] = {1, 2, 3, 4, 5};89 int b[5];90 std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =91 std::ranges::transform(In1(a), Sent1(In1(a + 5)), Out(b), [](int i) { return i * 2; });92 assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));93 assert(base(ret.in) == a + 5);94 assert(base(ret.out) == b + 5);95 }96 97 {98 int a[] = {1, 2, 3, 4, 5};99 int b[5];100 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));101 std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =102 std::ranges::transform(range, Out(b), [](int i) { return i * 2; });103 assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));104 assert(base(ret.in) == a + 5);105 assert(base(ret.out) == b + 5);106 }107 }108 109 { // first range empty110 {111 std::array<int, 0> a = {};112 int b[5];113 auto ret = std::ranges::transform(In1(a.data()), Sent1(In1(a.data())), Out(b), [](int i) { return i * 2; });114 assert(base(ret.in) == a.data());115 assert(base(ret.out) == b);116 }117 118 {119 std::array<int, 0> a = {};120 int b[5];121 auto range = std::ranges::subrange(In1(a.data()), Sent1(In1(a.data())));122 auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });123 assert(base(ret.in) == a.data());124 assert(base(ret.out) == b);125 }126 }127 128 { // one element range129 {130 int a[] = {2};131 int b[5];132 auto ret = std::ranges::transform(In1(a), Sent1(In1(a + 1)), Out(b), [](int i) { return i * 2; });133 assert(b[0] == 4);134 assert(base(ret.in) == a + 1);135 assert(base(ret.out) == b + 1);136 }137 138 {139 int a[] = {2};140 int b[5];141 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 1)));142 auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });143 assert(b[0] == 4);144 assert(base(ret.in) == a + 1);145 assert(base(ret.out) == b + 1);146 }147 }148 149 { // check that the transform function and projection call counts are correct150 {151 int predCount = 0;152 int projCount = 0;153 auto pred = [&](int) { ++predCount; return 1; };154 auto proj = [&](int) { ++projCount; return 0; };155 int a[] = {1, 2, 3, 4};156 std::array<int, 4> c;157 std::ranges::transform(In1(a), Sent1(In1(a + 4)), Out(c.data()), pred, proj);158 assert(predCount == 4);159 assert(projCount == 4);160 assert((c == std::array{1, 1, 1, 1}));161 }162 {163 int predCount = 0;164 int projCount = 0;165 auto pred = [&](int) { ++predCount; return 1; };166 auto proj = [&](int) { ++projCount; return 0; };167 int a[] = {1, 2, 3, 4};168 std::array<int, 4> c;169 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 4)));170 std::ranges::transform(range, Out(c.data()), pred, proj);171 assert(predCount == 4);172 assert(projCount == 4);173 assert((c == std::array{1, 1, 1, 1}));174 }175 }176 return true;177}178// clang-format on179 180template <class Out>181constexpr void test_iterator_in1() {182 test_iterators<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>();183 test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();184 test_iterators<forward_iterator<int*>, Out, forward_iterator<int*>>();185 test_iterators<bidirectional_iterator<int*>, Out, bidirectional_iterator<int*>>();186 test_iterators<random_access_iterator<int*>, Out, random_access_iterator<int*>>();187 test_iterators<contiguous_iterator<int*>, Out, contiguous_iterator<int*>>();188 test_iterators<int*, Out, int*>();189 // static_asserting here to avoid hitting the constant evaluation step limit190 static_assert(test_iterators<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>());191 static_assert(test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>());192 static_assert(test_iterators<forward_iterator<int*>, Out, forward_iterator<int*>>());193 static_assert(test_iterators<bidirectional_iterator<int*>, Out, bidirectional_iterator<int*>>());194 static_assert(test_iterators<random_access_iterator<int*>, Out, random_access_iterator<int*>>());195 static_assert(test_iterators<contiguous_iterator<int*>, Out, contiguous_iterator<int*>>());196 static_assert(test_iterators<int*, Out, int*>());197}198 199constexpr bool test() {200 { // check that std::ranges::dangling is returned properly201 std::array<int, 5> b;202 std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =203 std::ranges::transform(std::array{1, 2, 3, 5, 4}, b.data(), [](int i) { return i * i; });204 assert((b == std::array{1, 4, 9, 25, 16}));205 assert(ret.out == b.data() + b.size());206 }207 208 { // check that returning another type from the projection works209 {210 struct S { int i; int other; };211 S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };212 std::array<int, 4> b;213 std::ranges::transform(a, a + 4, b.begin(), [](S s) { return s.i; });214 assert((b == std::array{0, 1, 3, 10}));215 }216 {217 struct S { int i; int other; };218 S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };219 std::array<int, 4> b;220 std::ranges::transform(a, b.begin(), [](S s) { return s.i; });221 assert((b == std::array{0, 1, 3, 10}));222 }223 }224 225 { // check that std::invoke is used226 struct S { int i; };227 S a[] = { S{1}, S{3}, S{2} };228 std::array<int, 3> b;229 auto ret = std::ranges::transform(a, b.data(), [](int i) { return i; }, &S::i);230 assert((b == std::array{1, 3, 2}));231 assert(ret.out == b.data() + 3);232 }233 234 return true;235}236 237int main(int, char**) {238 test_iterator_in1<cpp17_output_iterator<int*>>();239 test_iterator_in1<cpp20_output_iterator<int*>>();240 test_iterator_in1<forward_iterator<int*>>();241 test_iterator_in1<bidirectional_iterator<int*>>();242 test_iterator_in1<random_access_iterator<int*>>();243 test_iterator_in1<contiguous_iterator<int*>>();244 test_iterator_in1<int*>();245 test();246 static_assert(test());247 248 return 0;249}250