xref: /llvm-project/libcxx/test/std/containers/sequences/from_range_sequence_containers.h (revision f73050e722dd2e484358d03674eb186f3a2f4799)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
10 #define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
11 
12 #include <algorithm>
13 #include <array>
14 #include <cassert>
15 #include <cstddef>
16 #include <iterator>
17 #include <ranges>
18 #include <utility>
19 
20 #include "../exception_safety_helpers.h"
21 #include "../from_range_helpers.h"
22 #include "MoveOnly.h"
23 #include "almost_satisfies_types.h"
24 #include "count_new.h"
25 #include "test_iterators.h"
26 #include "test_macros.h"
27 
28 template <class T>
29 concept HasSize = requires (const T& value) { value.size(); };
30 
31 template <class Container, class Range>
32 concept HasFromRangeCtr = requires (Range&& range) {
33   Container(std::from_range, std::forward<Range>(range));
34   Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
35 };
36 
37 template <template <class...> class Container, class T, class U>
38 constexpr bool test_constraints() {
39   // Input range with the same value type.
40   static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
41   // Input range with a convertible value type.
42   static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
43   // Input range with a non-convertible value type.
44   static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
45   // Not an input range.
46   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
47   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
48   static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
49 
50   // Note: there are no constraints on the allocator (it's not a separate template type of the constructor)`.
51 
52   return true;
53 }
54 
55 // Note: `std::array` is used to avoid dealing with `vector<bool>`.
56 template <template <class ...> class Container,
57           class T,
58           class Iter,
59           class Sent,
60           class Alloc,
61           std::size_t N,
62           class ValidateFunc>
63 constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) {
64   { // (range)
65     auto in = wrap_input<Iter, Sent>(input);
66     Container<T> c(std::from_range, in);
67 
68     if constexpr (HasSize<Container<T>>) {
69       assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
70     }
71     assert(std::ranges::equal(input, c));
72     validate(c);
73   }
74 
75   { // (range, allocator)
76     auto in = wrap_input<Iter, Sent>(input);
77     Alloc alloc;
78     Container<T, Alloc> c(std::from_range, in, alloc);
79 
80     assert(c.get_allocator() == alloc);
81     if constexpr (HasSize<Container<T, Alloc>>) {
82       assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
83     }
84     assert(std::ranges::equal(input, c));
85     validate(c);
86   }
87 }
88 
89 template <template <class ...> class Container,
90           class T,
91           class Iter,
92           class Sent,
93           class Alloc,
94           class ValidateFunc>
95 constexpr void test_sequence_container(ValidateFunc validate) {
96   // Normal input.
97   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate);
98   // Empty input.
99   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate);
100   // Single-element input.
101   test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate);
102 }
103 
104 template <template <class ...> class Container>
105 constexpr void test_sequence_container_move_only() {
106   MoveOnly input[5];
107   std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
108 
109   [[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
110 }
111 
112 template <class Iter,
113           class Sent,
114           class Alloc,
115           class ValidateFunc>
116 constexpr void test_vector_bool(ValidateFunc validate) {
117   // Normal input.
118   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(
119       std::array{true, false, false, true, false, true, true, true, false, true}, validate);
120   // Empty input.
121   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate);
122   // Single-element input.
123   test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate);
124 }
125 
126 template <template <class ...> class Container>
127 void test_exception_safety_throwing_copy() {
128 #if !defined(TEST_HAS_NO_EXCEPTIONS)
129   constexpr int ThrowOn = 3;
130   using T = ThrowingCopy<ThrowOn>;
131   test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) {
132     [[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to));
133   });
134 #endif
135 }
136 
137 template <template <class ...> class Container, class T>
138 void test_exception_safety_throwing_allocator() {
139 #if !defined(TEST_HAS_NO_EXCEPTIONS)
140   T in[] = {0, 1};
141 
142   try {
143     ThrowingAllocator<T> alloc;
144 
145     globalMemCounter.reset();
146     Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc);
147     assert(false); // The constructor call above should throw.
148 
149   } catch (int) {
150     assert(globalMemCounter.new_called == globalMemCounter.delete_called);
151   }
152 #endif
153 }
154 
155 #endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H
156