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 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
10 | |
11 | // Test that `ranges::to` can be used to convert between arbitrary standard containers. |
12 | |
13 | #include <ranges> |
14 | |
15 | #include <algorithm> |
16 | #include <cassert> |
17 | #include <deque> |
18 | #include <forward_list> |
19 | #include <list> |
20 | #include <map> |
21 | #include <queue> |
22 | #include <set> |
23 | #include <stack> |
24 | #include <string> |
25 | #include <unordered_map> |
26 | #include <unordered_set> |
27 | #include <vector> |
28 | |
29 | #include "test_iterators.h" |
30 | #include "test_range.h" |
31 | #include "type_algorithms.h" |
32 | #include "unwrap_container_adaptor.h" |
33 | |
34 | std::vector<std::vector<int>> ints = { |
35 | {5, 1, 3, 4, 2}, |
36 | {3}, |
37 | {} |
38 | }; |
39 | |
40 | std::vector<std::vector<char>> chars = { |
41 | {'a', 'b', 'c'}, |
42 | {'a'}, |
43 | {} |
44 | }; |
45 | |
46 | std::vector<std::vector<std::pair<const int, int>>> pairs = { |
47 | {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}}, |
48 | {{1, 2}}, |
49 | {} |
50 | }; |
51 | |
52 | template <class From, class To> |
53 | void test_is_equal(std::vector<std::vector<typename From::value_type>> inputs) { |
54 | for (const auto& in : inputs) { |
55 | From from(in.begin(), in.end()); |
56 | std::same_as<To> decltype(auto) result = std::ranges::to<To>(from); |
57 | assert(std::ranges::equal(in, result)); |
58 | } |
59 | } |
60 | |
61 | template <class From, class To> |
62 | void test_is_permutation(std::vector<std::vector<typename From::value_type>> inputs) { |
63 | for (const auto& in : inputs) { |
64 | From from(in.begin(), in.end()); |
65 | std::same_as<To> decltype(auto) result = std::ranges::to<To>(in); |
66 | assert(std::ranges::is_permutation(in, result)); |
67 | } |
68 | } |
69 | |
70 | template <class From, class To> |
71 | void (std::vector<std::vector<typename From::value_type>> inputs) { |
72 | for (const auto& in : inputs) { |
73 | From from(in.begin(), in.end()); |
74 | std::same_as<To> decltype(auto) result = std::ranges::to<To>(in); |
75 | |
76 | UnwrapAdaptor<From> unwrap_from(std::move(from)); |
77 | UnwrapAdaptor<To> unwrap_to(std::move(result)); |
78 | assert(std::ranges::is_permutation(unwrap_from.get_container(), unwrap_to.get_container())); |
79 | } |
80 | } |
81 | |
82 | template <class T> |
83 | using sequence_containers = types::type_list< |
84 | std::vector<T>, |
85 | std::deque<T>, |
86 | std::list<T>, |
87 | std::forward_list<T> |
88 | >; |
89 | |
90 | template <class T> |
91 | using associative_sets = types::type_list< |
92 | std::set<T>, |
93 | std::multiset<T> |
94 | >; |
95 | |
96 | template <class K, class V> |
97 | using associative_maps = types::type_list< |
98 | std::map<K, V>, |
99 | std::multimap<K, V> |
100 | >; |
101 | |
102 | template <class T> |
103 | using unordered_sets = types::type_list< |
104 | std::unordered_set<T>, |
105 | std::unordered_multiset<T> |
106 | >; |
107 | |
108 | template <class K, class V> |
109 | using unordered_maps = types::type_list< |
110 | std::unordered_map<K, V>, |
111 | std::unordered_multimap<K, V> |
112 | >; |
113 | |
114 | template <class T> |
115 | using container_adaptors = types::type_list< |
116 | std::stack<T>, |
117 | std::queue<T>, |
118 | std::priority_queue<T> |
119 | >; |
120 | |
121 | template <class T> |
122 | using sequences_and_sets = types::concatenate_t<sequence_containers<T>, associative_sets<T>, unordered_sets<T>>; |
123 | |
124 | template <class K, class V> |
125 | using all_containers = types::concatenate_t< |
126 | sequence_containers<std::pair<const K, V>>, |
127 | associative_sets<std::pair<const K, V>>, |
128 | associative_maps<K, V>, |
129 | unordered_sets<std::pair<const K, V>>, |
130 | unordered_maps<K, V>>; |
131 | |
132 | // This is necessary to be able to use `pair`s with unordered sets. |
133 | template <class K, class V> |
134 | struct std::hash<std::pair<const K, V>> { |
135 | std::size_t operator()(const std::pair<const K, V>& p) const { |
136 | std::size_t h1 = std::hash<K>{}(p.first); |
137 | std::size_t h2 = std::hash<V>{}(p.second); |
138 | return h1 ^ (h2 << 1); |
139 | } |
140 | }; |
141 | |
142 | void test() { |
143 | { // Conversions always preserving equality. |
144 | { // sequences <-> sequences |
145 | types::for_each(sequence_containers<int>{}, []<class From>() { |
146 | types::for_each(sequence_containers<int>{}, []<class To>() { |
147 | test_is_equal<From, To>(ints); |
148 | }); |
149 | }); |
150 | |
151 | types::for_each(sequence_containers<int>{}, []<class From>() { |
152 | types::for_each(sequence_containers<double>{}, []<class To>() { |
153 | test_is_equal<From, To>(ints); |
154 | }); |
155 | }); |
156 | } |
157 | |
158 | { // sequences <-> string |
159 | types::for_each(sequence_containers<char>{}, []<class Seq>() { |
160 | test_is_equal<Seq, std::basic_string<char>>(chars); |
161 | test_is_equal<std::basic_string<char>, Seq>(chars); |
162 | }); |
163 | } |
164 | } |
165 | |
166 | { // sequences/sets <-> sequences/sets |
167 | types::for_each(sequences_and_sets<int>{}, []<class From>() { |
168 | types::for_each(sequences_and_sets<int>{}, []<class To>() { |
169 | test_is_permutation<From, To>(ints); |
170 | }); |
171 | }); |
172 | |
173 | types::for_each(sequences_and_sets<int>{}, []<class From>() { |
174 | types::for_each(sequences_and_sets<double>{}, []<class To>() { |
175 | test_is_permutation<From, To>(ints); |
176 | }); |
177 | }); |
178 | } |
179 | |
180 | { // sequences/sets/maps <-> sequences/sets/maps. Uses `pair` for non-map containers to allow mutual conversion with |
181 | // map types. |
182 | types::for_each(all_containers<int, int>{}, []<class From>() { |
183 | types::for_each(all_containers<int, int>{}, []<class To>() { |
184 | test_is_permutation<From, To>(pairs); |
185 | }); |
186 | }); |
187 | |
188 | types::for_each(all_containers<int, int>{}, []<class From>() { |
189 | types::for_each(all_containers<long, double>{}, []<class To>() { |
190 | test_is_permutation<From, To>(pairs); |
191 | }); |
192 | }); |
193 | } |
194 | |
195 | { // adaptors <-> adaptors |
196 | types::for_each(container_adaptors<int>{}, []<class From>() { |
197 | types::for_each(container_adaptors<int>{}, []<class To>() { |
198 | test_is_equal_for_adaptors<From, To>(ints); |
199 | }); |
200 | }); |
201 | |
202 | types::for_each(container_adaptors<int>{}, []<class From>() { |
203 | types::for_each(container_adaptors<double>{}, []<class To>() { |
204 | test_is_equal_for_adaptors<From, To>(ints); |
205 | }); |
206 | }); |
207 | } |
208 | } |
209 | |
210 | int main(int, char**) { |
211 | test(); |
212 | |
213 | return 0; |
214 | } |
215 | |