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