//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // // template S, copy_constructible F> // requires invocable && indirectly_writable> // constexpr O generate(O first, S last, F gen); // Since C++20 // // template // requires invocable && output_range> // constexpr borrowed_iterator_t generate(R&& r, F gen); // Since C++20 #include #include #include #include #include #include #include "almost_satisfies_types.h" #include "test_iterators.h" struct IntGen { int operator()() const; }; struct UncopyableGen { UncopyableGen(const UncopyableGen&) = delete; int operator()() const; }; static_assert(!std::copy_constructible); static_assert(std::invocable); struct UninvocableGen { }; static_assert(std::copy_constructible); static_assert(!std::invocable); struct IntPtrGen { int* operator()() const; }; // Test constraints of the (iterator, sentinel) overload. // ====================================================== template concept HasGenerateIter = requires(Iter&& iter, Sent&& sent, Gen&& gen) { std::ranges::generate(std::forward(iter), std::forward(sent), std::forward(gen)); }; static_assert(HasGenerateIter); // !input_or_output_iterator static_assert(!HasGenerateIter); // !sentinel_for static_assert(!HasGenerateIter); static_assert(!HasGenerateIter); // !copy_constructible static_assert(!HasGenerateIter); // !invocable static_assert(!HasGenerateIter); // !indirectly_writable> static_assert(!HasGenerateIter); // Test constraints of the (range) overload. // ========================================= template concept HasGenerateRange = requires(Range&& range, Gen&& gen) { std::ranges::generate(std::forward(range), std::forward(gen)); }; template using R = UncheckedRange; static_assert(HasGenerateRange, IntGen>); // !copy_constructible static_assert(!HasGenerateRange, UncopyableGen>); // !invocable static_assert(!HasGenerateRange, UninvocableGen>); // !output_range> static_assert(!HasGenerateRange); static_assert(!HasGenerateRange, IntPtrGen>); template constexpr void test_one(const std::array input, Gen gen, std::array expected) { { // (iterator, sentinel) overload. auto in = input; auto begin = Iter(in.data()); auto end = Sent(Iter(in.data() + in.size())); std::same_as decltype(auto) result = std::ranges::generate(std::move(begin), std::move(end), gen); assert(base(result) == in.data() + in.size()); assert(in == expected); } { // (range) overload. auto in = input; auto begin = Iter(in.data()); auto end = Sent(Iter(in.data() + in.size())); auto range = std::ranges::subrange(std::move(begin), std::move(end)); // For some reason `ranges::generate` accepts both input and output iterators but only output (not input) ranges. if constexpr (std::ranges::output_range>) { std::same_as decltype(auto) result = std::ranges::generate(std::move(range), gen); assert(base(result) == in.data() + in.size()); assert(in == expected); } } } template constexpr void test_iter_sent() { auto gen = [ctr = 1] () mutable { return ctr++; }; // Empty sequence. test_one({}, gen, {}); // 1-element sequence. test_one(std::array{-10}, gen, {1}); // Longer sequence. test_one(std::array{}, gen, {1, 2, 3, 4, 5}); } template constexpr void test_iter() { if constexpr (std::sentinel_for) { test_iter_sent(); } test_iter_sent>(); } constexpr void test_iterators() { test_iter>(); test_iter>(); test_iter>(); test_iter>(); test_iter>(); test_iter>(); test_iter>(); test_iter>(); test_iter(); } constexpr bool test() { test_iterators(); { // Complexity: exactly N evaluations of `gen()` and assignments. struct AssignedOnce { bool assigned = false; constexpr AssignedOnce& operator=(const AssignedOnce&) { assert(!assigned); assigned = true; return *this; } }; { // (iterator, sentinel) overload. int gen_invocations = 0; auto gen = [&gen_invocations] { ++gen_invocations; return AssignedOnce(); }; constexpr std::size_t N = 10; std::array in; std::ranges::generate(in.begin(), in.end(), gen); assert(std::ranges::all_of(in, &AssignedOnce::assigned)); assert(gen_invocations == N); } { // (range) overload. int gen_invocations = 0; auto gen = [&gen_invocations] { ++gen_invocations; return AssignedOnce(); }; constexpr std::size_t N = 10; std::array in; std::ranges::generate(in, gen); assert(std::ranges::all_of(in, &AssignedOnce::assigned)); assert(gen_invocations == N); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }