xref: /llvm-project/libcxx/test/std/containers/sequences/from_range_sequence_containers.h (revision f73050e722dd2e484358d03674eb186f3a2f4799)
117bbb224Svarconst //===----------------------------------------------------------------------===//
217bbb224Svarconst //
317bbb224Svarconst // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
417bbb224Svarconst // See https://llvm.org/LICENSE.txt for license information.
517bbb224Svarconst // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
617bbb224Svarconst //
717bbb224Svarconst //===----------------------------------------------------------------------===//
817bbb224Svarconst 
917bbb224Svarconst #ifndef SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
1017bbb224Svarconst #define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
1117bbb224Svarconst 
1217bbb224Svarconst #include <algorithm>
1317bbb224Svarconst #include <array>
1417bbb224Svarconst #include <cassert>
1517bbb224Svarconst #include <cstddef>
1617bbb224Svarconst #include <iterator>
1717bbb224Svarconst #include <ranges>
1817bbb224Svarconst #include <utility>
1917bbb224Svarconst 
20d0b51657Svarconst #include "../exception_safety_helpers.h"
2117bbb224Svarconst #include "../from_range_helpers.h"
2217bbb224Svarconst #include "MoveOnly.h"
2317bbb224Svarconst #include "almost_satisfies_types.h"
2417bbb224Svarconst #include "count_new.h"
2517bbb224Svarconst #include "test_iterators.h"
2617bbb224Svarconst #include "test_macros.h"
2717bbb224Svarconst 
2817bbb224Svarconst template <class T>
2917bbb224Svarconst concept HasSize = requires (const T& value) { value.size(); };
3017bbb224Svarconst 
3117bbb224Svarconst template <class Container, class Range>
3217bbb224Svarconst concept HasFromRangeCtr = requires (Range&& range) {
3317bbb224Svarconst   Container(std::from_range, std::forward<Range>(range));
3417bbb224Svarconst   Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
3517bbb224Svarconst };
3617bbb224Svarconst 
3717bbb224Svarconst template <template <class...> class Container, class T, class U>
3817bbb224Svarconst constexpr bool test_constraints() {
3917bbb224Svarconst   // Input range with the same value type.
4017bbb224Svarconst   static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
4117bbb224Svarconst   // Input range with a convertible value type.
4217bbb224Svarconst   static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
4317bbb224Svarconst   // Input range with a non-convertible value type.
4417bbb224Svarconst   static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
4517bbb224Svarconst   // Not an input range.
4617bbb224Svarconst   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
4717bbb224Svarconst   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
4817bbb224Svarconst   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
4917bbb224Svarconst 
5017bbb224Svarconst   // Note: there are no constraints on the allocator (it's not a separate template type of the constructor)`.
5117bbb224Svarconst 
5217bbb224Svarconst   return true;
5317bbb224Svarconst }
5417bbb224Svarconst 
5517bbb224Svarconst // Note: `std::array` is used to avoid dealing with `vector<bool>`.
5617bbb224Svarconst template <template <class ...> class Container,
5717bbb224Svarconst           class T,
5817bbb224Svarconst           class Iter,
5917bbb224Svarconst           class Sent,
6017bbb224Svarconst           class Alloc,
6117bbb224Svarconst           std::size_t N,
6217bbb224Svarconst           class ValidateFunc>
6317bbb224Svarconst constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) {
6417bbb224Svarconst   { // (range)
65*f73050e7SLouis Dionne     auto in = wrap_input<Iter, Sent>(input);
6617bbb224Svarconst     Container<T> c(std::from_range, in);
6717bbb224Svarconst 
6817bbb224Svarconst     if constexpr (HasSize<Container<T>>) {
6917bbb224Svarconst       assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
7017bbb224Svarconst     }
71*f73050e7SLouis Dionne     assert(std::ranges::equal(input, c));
7217bbb224Svarconst     validate(c);
7317bbb224Svarconst   }
7417bbb224Svarconst 
7517bbb224Svarconst   { // (range, allocator)
76*f73050e7SLouis Dionne     auto in = wrap_input<Iter, Sent>(input);
7717bbb224Svarconst     Alloc alloc;
7817bbb224Svarconst     Container<T, Alloc> c(std::from_range, in, alloc);
7917bbb224Svarconst 
8017bbb224Svarconst     assert(c.get_allocator() == alloc);
8117bbb224Svarconst     if constexpr (HasSize<Container<T, Alloc>>) {
8217bbb224Svarconst       assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
8317bbb224Svarconst     }
84*f73050e7SLouis Dionne     assert(std::ranges::equal(input, c));
8517bbb224Svarconst     validate(c);
8617bbb224Svarconst   }
8717bbb224Svarconst }
8817bbb224Svarconst 
8917bbb224Svarconst template <template <class ...> class Container,
9017bbb224Svarconst           class T,
9117bbb224Svarconst           class Iter,
9217bbb224Svarconst           class Sent,
9317bbb224Svarconst           class Alloc,
9417bbb224Svarconst           class ValidateFunc>
9517bbb224Svarconst constexpr void test_sequence_container(ValidateFunc validate) {
9617bbb224Svarconst   // Normal input.
9717bbb224Svarconst   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate);
9817bbb224Svarconst   // Empty input.
9917bbb224Svarconst   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate);
10017bbb224Svarconst   // Single-element input.
10117bbb224Svarconst   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate);
10217bbb224Svarconst }
10317bbb224Svarconst 
10417bbb224Svarconst template <template <class ...> class Container>
10517bbb224Svarconst constexpr void test_sequence_container_move_only() {
10617bbb224Svarconst   MoveOnly input[5];
10717bbb224Svarconst   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
10817bbb224Svarconst 
10917bbb224Svarconst   [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
11017bbb224Svarconst }
11117bbb224Svarconst 
11217bbb224Svarconst template <class Iter,
11317bbb224Svarconst           class Sent,
11417bbb224Svarconst           class Alloc,
11517bbb224Svarconst           class ValidateFunc>
11617bbb224Svarconst constexpr void test_vector_bool(ValidateFunc validate) {
11717bbb224Svarconst   // Normal input.
11817bbb224Svarconst   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(
11917bbb224Svarconst       std::array{true, false, false, true, false, true, true, true, false, true}, validate);
12017bbb224Svarconst   // Empty input.
12117bbb224Svarconst   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate);
12217bbb224Svarconst   // Single-element input.
12317bbb224Svarconst   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate);
12417bbb224Svarconst }
12517bbb224Svarconst 
12617bbb224Svarconst template <template <class ...> class Container>
12717bbb224Svarconst void test_exception_safety_throwing_copy() {
12817bbb224Svarconst #if !defined(TEST_HAS_NO_EXCEPTIONS)
129d0b51657Svarconst   constexpr int ThrowOn = 3;
130d0b51657Svarconst   using T = ThrowingCopy<ThrowOn>;
131d0b51657Svarconst   test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
132d0b51657Svarconst     [[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to));
133d0b51657Svarconst   });
13417bbb224Svarconst #endif
13517bbb224Svarconst }
13617bbb224Svarconst 
13717bbb224Svarconst template <template <class ...> class Container, class T>
13817bbb224Svarconst void test_exception_safety_throwing_allocator() {
13917bbb224Svarconst #if !defined(TEST_HAS_NO_EXCEPTIONS)
14017bbb224Svarconst   T in[] = {0, 1};
14117bbb224Svarconst 
14217bbb224Svarconst   try {
14317bbb224Svarconst     ThrowingAllocator<T> alloc;
14417bbb224Svarconst 
14517bbb224Svarconst     globalMemCounter.reset();
14617bbb224Svarconst     Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc);
14717bbb224Svarconst     assert(false); // The constructor call above should throw.
14817bbb224Svarconst 
14917bbb224Svarconst   } catch (int) {
15017bbb224Svarconst     assert(globalMemCounter.new_called == globalMemCounter.delete_called);
15117bbb224Svarconst   }
15217bbb224Svarconst #endif
15317bbb224Svarconst }
15417bbb224Svarconst 
15517bbb224Svarconst #endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
156