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
28template <class T>
29concept HasSize = requires (const T& value) { value.size(); };
30
31template <class Container, class Range>
32concept 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
37template <template <class...> class Container, class T, class U>
38constexpr 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>`.
56template <template <class ...> class Container,
57 class T,
58 class Iter,
59 class Sent,
60 class Alloc,
61 std::size_t N,
62 class ValidateFunc>
63constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) {
64 auto in = wrap_input<Iter, Sent>(input);
65
66 { // (range)
67 Container<T> c(std::from_range, in);
68
69 if constexpr (HasSize<Container<T>>) {
70 assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
71 }
72 assert(std::ranges::equal(in, c));
73 validate(c);
74 }
75
76 { // (range, allocator)
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(in, c));
85 validate(c);
86 }
87}
88
89template <template <class ...> class Container,
90 class T,
91 class Iter,
92 class Sent,
93 class Alloc,
94 class ValidateFunc>
95constexpr 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
104template <template <class ...> class Container>
105constexpr 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
112template <class Iter,
113 class Sent,
114 class Alloc,
115 class ValidateFunc>
116constexpr 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
126template <template <class ...> class Container>
127void 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
137template <template <class ...> class Container, class T>
138void 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

source code of libcxx/test/std/containers/sequences/from_range_sequence_containers.h