234 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++17, c++2010 11// <ranges>12 13// std::views::chunk_by14 15#include <ranges>16 17#include <algorithm>18#include <cassert>19#include <concepts>20#include <initializer_list>21#include <type_traits>22#include <utility>23 24#include "test_iterators.h"25#include "test_range.h"26 27struct Pred {28 constexpr bool operator()(int x, int y) const { return x != -y; }29};30 31struct NonCopyablePredicate : Pred {32 NonCopyablePredicate(NonCopyablePredicate const&) = delete;33};34 35struct Range : std::ranges::view_base {36 using Iterator = forward_iterator<int*>;37 using Sentinel = sentinel_wrapper<Iterator>;38 constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}39 constexpr Iterator begin() const { return Iterator(begin_); }40 constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }41 42private:43 int* begin_;44 int* end_;45};46 47template <typename View>48constexpr void compareViews(View v, std::initializer_list<std::initializer_list<int>> list) {49 auto b1 = v.begin();50 auto e1 = v.end();51 auto b2 = list.begin();52 auto e2 = list.end();53 for (; b1 != e1 && b2 != e2; ++b1, ++b2) {54 bool eq = std::ranges::equal(*b1, *b2, [](int x, int y) {55 assert(x == y);56 return true;57 });58 assert(eq);59 }60 assert(b1 == e1);61 assert(b2 == e2);62}63 64constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }65 66template <class T>67constexpr const T&& asConstRvalue(T&& t) {68 return static_cast<T const&&>(t);69}70 71constexpr bool test() {72 int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4};73 74 // Test range adaptor object75 {76 using RangeAdaptorObject = decltype(std::views::chunk_by);77 static_assert(std::is_const_v<RangeAdaptorObject>);78 79 // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular80 static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);81 }82 83 // Test `views::chunk_by(pred)(v)`84 {85 using Result = std::ranges::chunk_by_view<Range, Pred>;86 Range const range(buff, buff + 8);87 Pred pred;88 89 {90 // 'views::chunk_by(pred)' - &&91 std::same_as<Result> decltype(auto) result = std::views::chunk_by(pred)(range);92 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});93 }94 {95 // 'views::chunk_by(pred)' - const&&96 std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::chunk_by(pred))(range);97 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});98 }99 {100 // 'views::chunk_by(pred)' - &101 auto partial = std::views::chunk_by(pred);102 std::same_as<Result> decltype(auto) result = partial(range);103 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});104 }105 {106 // 'views::chunk_by(pred)' - const&107 auto const partial = std::views::chunk_by(pred);108 std::same_as<Result> decltype(auto) result = partial(range);109 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});110 }111 }112 113 // Test `v | views::chunk_by(pred)`114 {115 using Result = std::ranges::chunk_by_view<Range, Pred>;116 Range const range(buff, buff + 8);117 Pred pred;118 119 {120 // 'views::chunk_by(pred)' - &&121 std::same_as<Result> decltype(auto) result = range | std::views::chunk_by(pred);122 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});123 }124 {125 // 'views::chunk_by(pred)' - const&&126 std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::chunk_by(pred));127 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});128 }129 {130 // 'views::chunk_by(pred)' - &131 auto partial = std::views::chunk_by(pred);132 std::same_as<Result> decltype(auto) result = range | partial;133 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});134 }135 {136 // 'views::chunk_by(pred)' - const&137 auto const partial = std::views::chunk_by(pred);138 std::same_as<Result> decltype(auto) result = range | partial;139 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});140 }141 }142 143 // Test `views::chunk_by(v, pred)` range adaptor object144 {145 using Result = std::ranges::chunk_by_view<Range, Pred>;146 Range const range(buff, buff + 8);147 Pred pred;148 149 {150 // 'views::chunk_by' - &&151 auto range_adaptor = std::views::chunk_by;152 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);153 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});154 }155 {156 // 'views::chunk_by' - const&&157 auto const range_adaptor = std::views::chunk_by;158 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);159 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});160 }161 {162 // 'views::chunk_by' - &163 auto range_adaptor = std::views::chunk_by;164 std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);165 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});166 }167 {168 // 'views::chunk_by' - const&169 auto const range_adaptor = std::views::chunk_by;170 std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);171 compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});172 }173 }174 175 // Test that one can call std::views::chunk_by with arbitrary stuff, as long as we176 // don't try to actually complete the call by passing it a range.177 //178 // That makes no sense and we can't do anything with the result, but it's valid.179 {180 int array[3] = {1, 2, 3};181 [[maybe_unused]] auto partial = std::views::chunk_by(std::move(array));182 }183 184 // Test `adaptor | views::chunk_by(pred)`185 {186 Range const range(buff, buff + 8);187 188 {189 auto pred1 = [](int i) { return absoluteValue(i) < 3; };190 Pred pred2;191 using Result = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;192 std::same_as<Result> decltype(auto) result = range | std::views::filter(pred1) | std::views::chunk_by(pred2);193 compareViews(result, {{-2, -1}, {1, 2}});194 }195 {196 auto pred1 = [](int i) { return absoluteValue(i) < 3; };197 Pred pred2;198 using Result = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;199 auto const partial = std::views::filter(pred1) | std::views::chunk_by(pred2);200 std::same_as<Result> decltype(auto) result = range | partial;201 compareViews(result, {{-2, -1}, {1, 2}});202 }203 }204 205 // Test SFINAE friendliness206 {207 struct NotAView {};208 struct NotInvocable {};209 210 static_assert(!CanBePiped<Range, decltype(std::views::chunk_by)>);211 static_assert(CanBePiped<Range, decltype(std::views::chunk_by(Pred{}))>);212 static_assert(!CanBePiped<NotAView, decltype(std::views::chunk_by(Pred{}))>);213 static_assert(!CanBePiped<std::initializer_list<int>, decltype(std::views::chunk_by(Pred{}))>);214 static_assert(!CanBePiped<Range, decltype(std::views::chunk_by(NotInvocable{}))>);215 216 static_assert(!std::is_invocable_v<decltype(std::views::chunk_by)>);217 static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Pred, Range>);218 static_assert(std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred>);219 static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred, Pred>);220 static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), NonCopyablePredicate>);221 }222 223 { static_assert(std::is_same_v<decltype(std::ranges::views::chunk_by), decltype(std::views::chunk_by)>); }224 225 return true;226}227 228int main(int, char**) {229 test();230 static_assert(test());231 232 return 0;233}234