xref: /llvm-project/libcxx/test/std/ranges/range.utility/range.utility.conv/to_std_containers.pass.cpp (revision c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3)
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>
test_is_equal(std::vector<std::vector<typename From::value_type>> inputs)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>
test_is_permutation(std::vector<std::vector<typename From::value_type>> inputs)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>
test_is_equal_for_adaptors(std::vector<std::vector<typename From::value_type>> inputs)71 void test_is_equal_for_adaptors(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>> {
operator ()std::hash135   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 
test()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 
main(int,char **)210 int main(int, char**) {
211   test();
212 
213   return 0;
214 }
215