361 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// REQUIRES: std-at-least-c++2310 11// <ranges>12 13// std::views::join_with_view14 15#include <ranges>16 17#include <memory>18#include <span>19#include <string_view>20#include <utility>21 22#include "test_iterators.h"23 24template <class View, class T>25concept CanBePiped = requires(View&& view, T&& t) {26 { std::forward<View>(view) | std::forward<T>(t) };27};28 29struct Range : std::ranges::view_base {30 using Iterator = forward_iterator<std::string_view*>;31 using Sentinel = sentinel_wrapper<Iterator>;32 constexpr explicit Range(std::string_view* b, std::string_view* e) : begin_(b), end_(e) {}33 constexpr Iterator begin() const { return Iterator(begin_); }34 constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }35 36private:37 std::string_view* begin_;38 std::string_view* end_;39};40 41struct Pattern : std::ranges::view_base {42 using Iterator = forward_iterator<const char*>;43 using Sentinel = sentinel_wrapper<Iterator>;44 static constexpr std::string_view pat{", "};45 46 constexpr Pattern() = default;47 constexpr Iterator begin() const { return Iterator(pat.data()); }48 constexpr Sentinel end() const { return Sentinel(Iterator(pat.data() + pat.size())); }49};50 51struct NonCopyablePattern : Pattern {52 NonCopyablePattern(const NonCopyablePattern&) = delete;53};54 55template <typename View>56constexpr void compareViews(View v, std::string_view list) {57 auto b1 = v.begin();58 auto e1 = v.end();59 auto b2 = list.begin();60 auto e2 = list.end();61 for (; b1 != e1 && b2 != e2; ++b1, ++b2) {62 assert(*b1 == *b2);63 }64 assert(b1 == e1);65 assert(b2 == e2);66}67 68constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }69 70template <class T>71constexpr const T&& asConstRvalue(T&& t) {72 return static_cast<const T&&>(t);73}74 75constexpr void test_adaptor_with_pattern(std::span<std::string_view> buff) {76 // Test `views::join_with(pattern)(v)`77 {78 using Result = std::ranges::join_with_view<Range, Pattern>;79 const Range range(buff.data(), buff.data() + buff.size());80 Pattern pattern;81 82 {83 // 'views::join_with(pattern)' - &&84 std::same_as<Result> decltype(auto) result = std::views::join_with(pattern)(range);85 compareViews(result, "abcd, ef, ghij, kl");86 }87 {88 // 'views::join_with(pattern)' - const&&89 std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(pattern))(range);90 compareViews(result, "abcd, ef, ghij, kl");91 }92 {93 // 'views::join_with(pattern)' - &94 auto partial = std::views::join_with(pattern);95 std::same_as<Result> decltype(auto) result = partial(range);96 compareViews(result, "abcd, ef, ghij, kl");97 }98 {99 // 'views::join_with(pattern)' - const&100 auto const partial = std::views::join_with(pattern);101 std::same_as<Result> decltype(auto) result = partial(range);102 compareViews(result, "abcd, ef, ghij, kl");103 }104 }105 106 // Test `v | views::join_with(pattern)`107 {108 using Result = std::ranges::join_with_view<Range, Pattern>;109 const Range range(buff.data(), buff.data() + buff.size());110 Pattern pattern;111 112 {113 // 'views::join_with(pattern)' - &&114 std::same_as<Result> decltype(auto) result = range | std::views::join_with(pattern);115 compareViews(result, "abcd, ef, ghij, kl");116 }117 {118 // 'views::join_with(pattern)' - const&&119 std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(pattern));120 compareViews(result, "abcd, ef, ghij, kl");121 }122 {123 // 'views::join_with(pattern)' - &124 auto partial = std::views::join_with(pattern);125 std::same_as<Result> decltype(auto) result = range | partial;126 compareViews(result, "abcd, ef, ghij, kl");127 }128 {129 // 'views::join_with(pattern)' - const&130 auto const partial = std::views::join_with(pattern);131 std::same_as<Result> decltype(auto) result = range | partial;132 compareViews(result, "abcd, ef, ghij, kl");133 }134 }135 136 // Test `views::join_with(v, pattern)` range adaptor object137 {138 using Result = std::ranges::join_with_view<Range, Pattern>;139 const Range range(buff.data(), buff.data() + buff.size());140 Pattern pattern;141 142 {143 // 'views::join_with' - &&144 auto range_adaptor = std::views::join_with;145 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);146 compareViews(result, "abcd, ef, ghij, kl");147 }148 {149 // 'views::join_with' - const&&150 const auto range_adaptor = std::views::join_with;151 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);152 compareViews(result, "abcd, ef, ghij, kl");153 }154 {155 // 'views::join_with' - &156 auto range_adaptor = std::views::join_with;157 std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);158 compareViews(result, "abcd, ef, ghij, kl");159 }160 {161 // 'views::join_with' - const&162 const auto range_adaptor = std::views::join_with;163 std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);164 compareViews(result, "abcd, ef, ghij, kl");165 }166 }167 168 // Test `adaptor | views::join_with(pattern)`169 {170 auto pred = [](std::string_view s) { return s.size() >= 3; };171 using Result = std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, Pattern>;172 const Range range(buff.data(), buff.data() + buff.size());173 Pattern pattern;174 175 {176 std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(pattern);177 compareViews(result, "abcd, ghij");178 }179 {180 const auto partial = std::views::filter(pred) | std::views::join_with(pattern);181 std::same_as<Result> decltype(auto) result = range | partial;182 compareViews(result, "abcd, ghij");183 }184 }185}186 187constexpr void test_adaptor_with_single_element(std::span<std::string_view> buff) {188 // Test `views::join_with(element)(v)`189 {190 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;191 const Range range(buff.data(), buff.data() + buff.size());192 const char element = '.';193 194 {195 // 'views::join_with(element)' - &&196 std::same_as<Result> decltype(auto) result = std::views::join_with(element)(range);197 compareViews(result, "abcd.ef.ghij.kl");198 }199 {200 // 'views::join_with(element)' - const&&201 std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(element))(range);202 compareViews(result, "abcd.ef.ghij.kl");203 }204 {205 // 'views::join_with(element)' - &206 auto partial = std::views::join_with(element);207 std::same_as<Result> decltype(auto) result = partial(range);208 compareViews(result, "abcd.ef.ghij.kl");209 }210 {211 // 'views::join_with(element)' - const&212 const auto partial = std::views::join_with(element);213 std::same_as<Result> decltype(auto) result = partial(range);214 compareViews(result, "abcd.ef.ghij.kl");215 }216 }217 218 // Test `v | views::join_with(element)`219 {220 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;221 const Range range(buff.data(), buff.data() + buff.size());222 const char element = '.';223 224 {225 // 'views::join_with(element)' - &&226 std::same_as<Result> decltype(auto) result = range | std::views::join_with(element);227 compareViews(result, "abcd.ef.ghij.kl");228 }229 {230 // 'views::join_with(element)' - const&&231 std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(element));232 compareViews(result, "abcd.ef.ghij.kl");233 }234 {235 // 'views::join_with(element)' - &236 auto partial = std::views::join_with(element);237 std::same_as<Result> decltype(auto) result = range | partial;238 compareViews(result, "abcd.ef.ghij.kl");239 }240 {241 // 'views::join_with(element)' - const&242 const auto partial = std::views::join_with(element);243 std::same_as<Result> decltype(auto) result = range | partial;244 compareViews(result, "abcd.ef.ghij.kl");245 }246 }247 248 // Test `views::join_with(v, element)` range adaptor object249 {250 using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;251 const Range range(buff.data(), buff.data() + buff.size());252 const char element = '.';253 254 {255 // 'views::join_with' - &&256 auto range_adaptor = std::views::join_with;257 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);258 compareViews(result, "abcd.ef.ghij.kl");259 }260 {261 // 'views::join_with' - const&&262 const auto range_adaptor = std::views::join_with;263 std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);264 compareViews(result, "abcd.ef.ghij.kl");265 }266 {267 // 'views::join_with' - &268 auto range_adaptor = std::views::join_with;269 std::same_as<Result> decltype(auto) result = range_adaptor(range, element);270 compareViews(result, "abcd.ef.ghij.kl");271 }272 {273 // 'views::join_with' - const&274 const auto range_adaptor = std::views::join_with;275 std::same_as<Result> decltype(auto) result = range_adaptor(range, element);276 compareViews(result, "abcd.ef.ghij.kl");277 }278 }279 280 // Test `adaptor | views::join_with(element)`281 {282 auto pred = [](std::string_view s) { return s.size() >= 3; };283 using Result =284 std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, std::ranges::single_view<char>>;285 const Range range(buff.data(), buff.data() + buff.size());286 const char element = '.';287 288 {289 std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(element);290 compareViews(result, "abcd.ghij");291 }292 {293 const auto partial = std::views::filter(pred) | std::views::join_with(element);294 std::same_as<Result> decltype(auto) result = range | partial;295 compareViews(result, "abcd.ghij");296 }297 }298}299 300constexpr bool test() {301 std::string_view buff[] = {"abcd", "ef", "ghij", "kl"};302 303 // Test range adaptor object304 {305 using RangeAdaptorObject = decltype(std::views::join_with);306 static_assert(std::is_const_v<RangeAdaptorObject>);307 308 // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular309 static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);310 }311 312 test_adaptor_with_pattern(buff);313 test_adaptor_with_single_element(buff);314 315 // Test that one can call std::views::join_with with arbitrary stuff, as long as we316 // don't try to actually complete the call by passing it a range.317 //318 // That makes no sense and we can't do anything with the result, but it's valid.319 {320 long array[3] = {1, 2, 3};321 [[maybe_unused]] auto partial = std::views::join_with(std::move(array));322 }323 324 // Test SFINAE friendliness325 {326 struct NotAView {};327 328 static_assert(!CanBePiped<Range, decltype(std::views::join_with)>);329 static_assert(CanBePiped<Range, decltype(std::views::join_with(Pattern{}))>);330 static_assert(CanBePiped<Range, decltype(std::views::join_with('.'))>);331 static_assert(!CanBePiped<NotAView, decltype(std::views::join_with(Pattern{}))>);332 static_assert(!CanBePiped<NotAView, decltype(std::views::join_with('.'))>);333 static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with(Pattern{}))>);334 static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with('.'))>);335 static_assert(!CanBePiped<Range, decltype(std::views::join_with(NotAView{}))>);336 337 static_assert(!std::is_invocable_v<decltype(std::views::join_with)>);338 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Pattern, Range>);339 static_assert(!std::is_invocable_v<decltype(std::views::join_with), char, Range>);340 static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, Pattern>);341 static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, char>);342 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, Pattern, Pattern>);343 static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, char, char>);344 static_assert(!std::is_invocable_v<decltype(std::views::join_with), NonCopyablePattern>);345 }346 347 {348 static_assert(std::is_same_v<decltype(std::ranges::views::join_with), decltype(std::views::join_with)>);349 assert(std::addressof(std::ranges::views::join_with) == std::addressof(std::views::join_with));350 }351 352 return true;353}354 355int main(int, char**) {356 test();357 static_assert(test());358 359 return 0;360}361