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 |
10 | |
11 | // <tuple> |
12 | |
13 | // template <class T, class Tuple> constexpr T make_from_tuple(Tuple&&); |
14 | |
15 | #include <tuple> |
16 | #include <array> |
17 | #include <utility> |
18 | #include <string> |
19 | #include <cassert> |
20 | |
21 | #include "test_macros.h" |
22 | #include "type_id.h" |
23 | |
24 | template <class Tuple> |
25 | struct ConstexprConstructibleFromTuple { |
26 | template <class ...Args> |
27 | explicit constexpr ConstexprConstructibleFromTuple(Args&&... xargs) |
28 | : args{std::forward<Args>(xargs)...} {} |
29 | Tuple args; |
30 | }; |
31 | |
32 | template <class TupleLike> |
33 | struct ConstructibleFromTuple; |
34 | |
35 | template <template <class ...> class Tuple, class ...Types> |
36 | struct ConstructibleFromTuple<Tuple<Types...>> { |
37 | template <class ...Args> |
38 | explicit ConstructibleFromTuple(Args&&... xargs) |
39 | : args(xargs...), |
40 | arg_types(&makeArgumentID<Args&&...>()) |
41 | {} |
42 | Tuple<std::decay_t<Types>...> args; |
43 | TypeID const* arg_types; |
44 | }; |
45 | |
46 | template <class Tp, std::size_t N> |
47 | struct ConstructibleFromTuple<std::array<Tp, N>> { |
48 | template <class ...Args> |
49 | explicit ConstructibleFromTuple(Args&&... xargs) |
50 | : args{xargs...}, |
51 | arg_types(&makeArgumentID<Args&&...>()) |
52 | {} |
53 | std::array<Tp, N> args; |
54 | TypeID const* arg_types; |
55 | }; |
56 | |
57 | template <class Tuple> |
58 | constexpr bool do_constexpr_test(Tuple&& tup) { |
59 | using RawTuple = std::decay_t<Tuple>; |
60 | using Tp = ConstexprConstructibleFromTuple<RawTuple>; |
61 | return std::make_from_tuple<Tp>(std::forward<Tuple>(tup)).args == tup; |
62 | } |
63 | |
64 | template <class ...ExpectTypes, class Tuple> |
65 | bool do_forwarding_test(Tuple&& tup) { |
66 | using RawTuple = std::decay_t<Tuple>; |
67 | using Tp = ConstructibleFromTuple<RawTuple>; |
68 | const Tp value = std::make_from_tuple<Tp>(std::forward<Tuple>(tup)); |
69 | return value.args == tup |
70 | && value.arg_types == &makeArgumentID<ExpectTypes...>(); |
71 | } |
72 | |
73 | void test_constexpr_construction() { |
74 | { |
75 | constexpr std::tuple<> tup; |
76 | static_assert(do_constexpr_test(tup), "" ); |
77 | } |
78 | { |
79 | constexpr std::tuple<int> tup(42); |
80 | static_assert(do_constexpr_test(tup), "" ); |
81 | } |
82 | { |
83 | constexpr std::tuple<int, long, void*> tup(42, 101, nullptr); |
84 | static_assert(do_constexpr_test(tup), "" ); |
85 | } |
86 | { |
87 | constexpr std::pair<int, const char*> p(42, "hello world" ); |
88 | static_assert(do_constexpr_test(tup: p), "" ); |
89 | } |
90 | { |
91 | using Tuple = std::array<int, 3>; |
92 | using ValueTp = ConstexprConstructibleFromTuple<Tuple>; |
93 | constexpr Tuple arr = {42, 101, -1}; |
94 | constexpr ValueTp value = std::make_from_tuple<ValueTp>(t: arr); |
95 | static_assert(value.args[0] == arr[0] && value.args[1] == arr[1] |
96 | && value.args[2] == arr[2], "" ); |
97 | } |
98 | } |
99 | |
100 | void test_perfect_forwarding() { |
101 | { |
102 | using Tup = std::tuple<>; |
103 | Tup tup; |
104 | Tup const& ctup = tup; |
105 | assert(do_forwarding_test<>(tup)); |
106 | assert(do_forwarding_test<>(ctup)); |
107 | } |
108 | { |
109 | using Tup = std::tuple<int>; |
110 | Tup tup(42); |
111 | Tup const& ctup = tup; |
112 | assert(do_forwarding_test<int&>(tup)); |
113 | assert(do_forwarding_test<int const&>(ctup)); |
114 | assert(do_forwarding_test<int&&>(std::move(tup))); |
115 | assert(do_forwarding_test<int const&&>(std::move(ctup))); |
116 | } |
117 | { |
118 | using Tup = std::tuple<int&, const char*, unsigned&&>; |
119 | int x = 42; |
120 | unsigned y = 101; |
121 | Tup tup(x, "hello world" , std::move(y)); |
122 | Tup const& ctup = tup; |
123 | assert((do_forwarding_test<int&, const char*&, unsigned&>(tup))); |
124 | assert((do_forwarding_test<int&, const char* const&, unsigned &>(ctup))); |
125 | assert((do_forwarding_test<int&, const char*&&, unsigned&&>(std::move(tup)))); |
126 | assert((do_forwarding_test<int&, const char* const&&, unsigned &&>(std::move(ctup)))); |
127 | } |
128 | // test with pair<T, U> |
129 | { |
130 | using Tup = std::pair<int&, const char*>; |
131 | int x = 42; |
132 | Tup tup(x, "hello world" ); |
133 | Tup const& ctup = tup; |
134 | assert((do_forwarding_test<int&, const char*&>(tup))); |
135 | assert((do_forwarding_test<int&, const char* const&>(ctup))); |
136 | assert((do_forwarding_test<int&, const char*&&>(std::move(tup)))); |
137 | assert((do_forwarding_test<int&, const char* const&&>(std::move(ctup)))); |
138 | } |
139 | // test with array<T, I> |
140 | { |
141 | using Tup = std::array<int, 3>; |
142 | Tup tup = {42, 101, -1}; |
143 | Tup const& ctup = tup; |
144 | assert((do_forwarding_test<int&, int&, int&>(tup))); |
145 | assert((do_forwarding_test<int const&, int const&, int const&>(ctup))); |
146 | assert((do_forwarding_test<int&&, int&&, int&&>(std::move(tup)))); |
147 | assert((do_forwarding_test<int const&&, int const&&, int const&&>(std::move(ctup)))); |
148 | } |
149 | } |
150 | |
151 | void test_noexcept() { |
152 | struct NothrowMoveable { |
153 | NothrowMoveable() = default; |
154 | NothrowMoveable(NothrowMoveable const&) {} |
155 | NothrowMoveable(NothrowMoveable&&) noexcept {} |
156 | }; |
157 | struct TestType { |
158 | TestType(int, NothrowMoveable) noexcept {} |
159 | TestType(int, int, int) noexcept(false) {} |
160 | TestType(long, long, long) noexcept {} |
161 | }; |
162 | { |
163 | using Tuple = std::tuple<int, NothrowMoveable>; |
164 | Tuple tup; ((void)tup); |
165 | Tuple const& ctup = tup; ((void)ctup); |
166 | ASSERT_NOT_NOEXCEPT(std::make_from_tuple<TestType>(t: ctup)); |
167 | LIBCPP_ASSERT_NOEXCEPT(std::make_from_tuple<TestType>(t: std::move(tup))); |
168 | } |
169 | { |
170 | using Tuple = std::pair<int, NothrowMoveable>; |
171 | Tuple tup; ((void)tup); |
172 | Tuple const& ctup = tup; ((void)ctup); |
173 | ASSERT_NOT_NOEXCEPT(std::make_from_tuple<TestType>(t: ctup)); |
174 | LIBCPP_ASSERT_NOEXCEPT(std::make_from_tuple<TestType>(t: std::move(tup))); |
175 | } |
176 | { |
177 | using Tuple = std::tuple<int, int, int>; |
178 | Tuple tup; ((void)tup); |
179 | ASSERT_NOT_NOEXCEPT(std::make_from_tuple<TestType>(t&: tup)); |
180 | } |
181 | { |
182 | using Tuple = std::tuple<long, long, long>; |
183 | Tuple tup; ((void)tup); |
184 | LIBCPP_ASSERT_NOEXCEPT(std::make_from_tuple<TestType>(t&: tup)); |
185 | } |
186 | { |
187 | using Tuple = std::array<int, 3>; |
188 | Tuple tup; ((void)tup); |
189 | ASSERT_NOT_NOEXCEPT(std::make_from_tuple<TestType>(t&: tup)); |
190 | } |
191 | { |
192 | using Tuple = std::array<long, 3>; |
193 | Tuple tup; ((void)tup); |
194 | LIBCPP_ASSERT_NOEXCEPT(std::make_from_tuple<TestType>(t&: tup)); |
195 | } |
196 | } |
197 | |
198 | namespace LWG3528 { |
199 | template <class T, class Tuple> |
200 | auto test_make_from_tuple(T&&, Tuple&& t) -> decltype(std::make_from_tuple<T>(t), uint8_t()) { |
201 | return 0; |
202 | } |
203 | template <class T, class Tuple> |
204 | uint32_t test_make_from_tuple(...) { |
205 | return 0; |
206 | } |
207 | |
208 | template <class T, class Tuple> |
209 | static constexpr bool can_make_from_tuple = |
210 | std::is_same_v<decltype(test_make_from_tuple<T, Tuple>(T{}, Tuple{})), uint8_t>; |
211 | |
212 | template <class T, class Tuple> |
213 | auto test_make_from_tuple_impl(T&&, Tuple&& t) |
214 | -> decltype(std::__make_from_tuple_impl<T>( |
215 | t, typename std::__make_tuple_indices< std::tuple_size_v<std::remove_reference_t<Tuple>>>::type{}), |
216 | uint8_t()) { |
217 | return 0; |
218 | } |
219 | template <class T, class Tuple> |
220 | uint32_t test_make_from_tuple_impl(...) { |
221 | return 0; |
222 | } |
223 | |
224 | template <class T, class Tuple> |
225 | static constexpr bool can_make_from_tuple_impl = |
226 | std::is_same_v<decltype(test_make_from_tuple_impl<T, Tuple>(T{}, Tuple{})), uint8_t>; |
227 | |
228 | struct A { |
229 | int a; |
230 | }; |
231 | struct B : public A {}; |
232 | |
233 | struct C { |
234 | C(const B&) {} |
235 | }; |
236 | |
237 | enum class D { |
238 | ONE, |
239 | TWO, |
240 | }; |
241 | |
242 | // Test std::make_from_tuple constraints. |
243 | |
244 | // reinterpret_cast |
245 | static_assert(!can_make_from_tuple<int*, std::tuple<A*>>); |
246 | static_assert(can_make_from_tuple<A*, std::tuple<A*>>); |
247 | |
248 | // const_cast |
249 | static_assert(!can_make_from_tuple<char*, std::tuple<const char*>>); |
250 | static_assert(!can_make_from_tuple<volatile char*, std::tuple<const volatile char*>>); |
251 | static_assert(can_make_from_tuple<volatile char*, std::tuple<volatile char*>>); |
252 | static_assert(can_make_from_tuple<char*, std::tuple<char*>>); |
253 | static_assert(can_make_from_tuple<const char*, std::tuple<char*>>); |
254 | static_assert(can_make_from_tuple<const volatile char*, std::tuple<volatile char*>>); |
255 | |
256 | // static_cast |
257 | static_assert(!can_make_from_tuple<int, std::tuple<D>>); |
258 | static_assert(!can_make_from_tuple<D, std::tuple<int>>); |
259 | static_assert(can_make_from_tuple<long, std::tuple<int>>); |
260 | static_assert(can_make_from_tuple<double, std::tuple<float>>); |
261 | static_assert(can_make_from_tuple<float, std::tuple<double>>); |
262 | |
263 | // Test std::__make_from_tuple_impl constraints. |
264 | |
265 | // reinterpret_cast |
266 | static_assert(!can_make_from_tuple_impl<int*, std::tuple<A*>>); |
267 | static_assert(can_make_from_tuple_impl<A*, std::tuple<A*>>); |
268 | |
269 | // const_cast |
270 | static_assert(!can_make_from_tuple_impl<char*, std::tuple<const char*>>); |
271 | static_assert(!can_make_from_tuple_impl<volatile char*, std::tuple<const volatile char*>>); |
272 | static_assert(can_make_from_tuple_impl<volatile char*, std::tuple<volatile char*>>); |
273 | static_assert(can_make_from_tuple_impl<char*, std::tuple<char*>>); |
274 | static_assert(can_make_from_tuple_impl<const char*, std::tuple<char*>>); |
275 | static_assert(can_make_from_tuple_impl<const volatile char*, std::tuple<volatile char*>>); |
276 | |
277 | // static_cast |
278 | static_assert(!can_make_from_tuple_impl<int, std::tuple<D>>); |
279 | static_assert(!can_make_from_tuple_impl<D, std::tuple<int>>); |
280 | static_assert(can_make_from_tuple_impl<long, std::tuple<int>>); |
281 | static_assert(can_make_from_tuple_impl<double, std::tuple<float>>); |
282 | static_assert(can_make_from_tuple_impl<float, std::tuple<double>>); |
283 | |
284 | } // namespace LWG3528 |
285 | |
286 | int main(int, char**) |
287 | { |
288 | test_constexpr_construction(); |
289 | test_perfect_forwarding(); |
290 | test_noexcept(); |
291 | |
292 | return 0; |
293 | } |
294 | |