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 F, class T> constexpr decltype(auto) apply(F &&, T &&) noexcept(see below) // noexcept since C++23
14
15// Test with different ref/ptr/cv qualified argument types.
16
17#include <tuple>
18#include <array>
19#include <utility>
20#include <cassert>
21
22#include "test_macros.h"
23#include "type_id.h"
24
25constexpr int constexpr_sum_fn() { return 0; }
26
27template <class ...Ints>
28constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); }
29
30struct ConstexprSumT {
31 constexpr ConstexprSumT() = default;
32 template <class ...Ints>
33 constexpr int operator()(Ints... values) const {
34 return constexpr_sum_fn(values...);
35 }
36};
37
38
39void test_constexpr_evaluation()
40{
41 constexpr ConstexprSumT sum_obj{};
42 {
43 using Tup = std::tuple<>;
44 using Fn = int(&)();
45 constexpr Tup t;
46 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 0, "");
47 static_assert(std::apply(f: sum_obj, t: t) == 0, "");
48 }
49 {
50 using Tup = std::tuple<int>;
51 using Fn = int(&)(int);
52 constexpr Tup t(42);
53 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 42, "");
54 static_assert(std::apply(f: sum_obj, t: t) == 42, "");
55 }
56 {
57 using Tup = std::tuple<int, long>;
58 using Fn = int(&)(int, int);
59 constexpr Tup t(42, 101);
60 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 143, "");
61 static_assert(std::apply(f: sum_obj, t: t) == 143, "");
62 }
63 {
64 using Tup = std::pair<int, long>;
65 using Fn = int(&)(int, int);
66 constexpr Tup t(42, 101);
67 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 143, "");
68 static_assert(std::apply(f: sum_obj, t: t) == 143, "");
69 }
70 {
71 using Tup = std::tuple<int, long, int>;
72 using Fn = int(&)(int, int, int);
73 constexpr Tup t(42, 101, -1);
74 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 142, "");
75 static_assert(std::apply(f: sum_obj, t: t) == 142, "");
76 }
77 {
78 using Tup = std::array<int, 3>;
79 using Fn = int(&)(int, int, int);
80 constexpr Tup t = {42, 101, -1};
81 static_assert(std::apply(f&: static_cast<Fn>(constexpr_sum_fn), t: t) == 142, "");
82 static_assert(std::apply(f: sum_obj, t: t) == 142, "");
83 }
84}
85
86
87enum CallQuals {
88 CQ_None,
89 CQ_LValue,
90 CQ_ConstLValue,
91 CQ_RValue,
92 CQ_ConstRValue
93};
94
95template <class Tuple>
96struct CallInfo {
97 CallQuals quals;
98 TypeID const* arg_types;
99 Tuple args;
100
101 template <class ...Args>
102 CallInfo(CallQuals q, Args&&... xargs)
103 : quals(q), arg_types(&makeArgumentID<Args&&...>()), args(std::forward<Args>(xargs)...)
104 {}
105};
106
107template <class ...Args>
108inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))>
109makeCallInfo(CallQuals quals, Args&&... args) {
110 return {quals, std::forward<Args>(args)...};
111}
112
113struct TrackedCallable {
114
115 TrackedCallable() = default;
116
117 template <class ...Args> auto operator()(Args&&... xargs) &
118 { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); }
119
120 template <class ...Args> auto operator()(Args&&... xargs) const&
121 { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); }
122
123 template <class ...Args> auto operator()(Args&&... xargs) &&
124 { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); }
125
126 template <class ...Args> auto operator()(Args&&... xargs) const&&
127 { return makeCallInfo(CQ_ConstRValue, std::forward<Args>(xargs)...); }
128};
129
130template <class ...ExpectArgs, class Tuple>
131void check_apply_quals_and_types(Tuple&& t) {
132 TypeID const* const expect_args = &makeArgumentID<ExpectArgs...>();
133 TrackedCallable obj;
134 TrackedCallable const& cobj = obj;
135 {
136 auto ret = std::apply(obj, std::forward<Tuple>(t));
137 assert(ret.quals == CQ_LValue);
138 assert(ret.arg_types == expect_args);
139 assert(ret.args == t);
140 }
141 {
142 auto ret = std::apply(cobj, std::forward<Tuple>(t));
143 assert(ret.quals == CQ_ConstLValue);
144 assert(ret.arg_types == expect_args);
145 assert(ret.args == t);
146 }
147 {
148 auto ret = std::apply(std::move(obj), std::forward<Tuple>(t));
149 assert(ret.quals == CQ_RValue);
150 assert(ret.arg_types == expect_args);
151 assert(ret.args == t);
152 }
153 {
154 auto ret = std::apply(std::move(cobj), std::forward<Tuple>(t));
155 assert(ret.quals == CQ_ConstRValue);
156 assert(ret.arg_types == expect_args);
157 assert(ret.args == t);
158 }
159}
160
161void test_call_quals_and_arg_types()
162{
163 using Tup = std::tuple<int, int const&, unsigned&&>;
164 const int x = 42;
165 unsigned y = 101;
166 Tup t(-1, x, std::move(y));
167 Tup const& ct = t;
168 check_apply_quals_and_types<int&, int const&, unsigned&>(t);
169 check_apply_quals_and_types<int const&, int const&, unsigned&>(t: ct);
170 check_apply_quals_and_types<int&&, int const&, unsigned&&>(t: std::move(t));
171 check_apply_quals_and_types<int const&&, int const&, unsigned&&>(t: std::move(ct));
172}
173
174
175struct NothrowMoveable {
176 NothrowMoveable() noexcept = default;
177 NothrowMoveable(NothrowMoveable const&) noexcept(false) {}
178 NothrowMoveable(NothrowMoveable&&) noexcept {}
179};
180
181template <bool IsNoexcept>
182struct TestNoexceptCallable {
183 template <class ...Args>
184 NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }
185};
186
187void test_noexcept()
188{
189 TestNoexceptCallable<true> nec;
190 TestNoexceptCallable<false> tc;
191 {
192 // test that the functions noexcept-ness is propagated
193 using Tup = std::tuple<int, const char*, long>;
194 Tup t;
195#if TEST_STD_VER >= 23
196 ASSERT_NOEXCEPT(std::apply(nec, t));
197#else
198 LIBCPP_ASSERT_NOEXCEPT(std::apply(f&: nec, t&: t));
199#endif
200 ASSERT_NOT_NOEXCEPT(std::apply(f&: tc, t&: t));
201 }
202 {
203 // test that the noexcept-ness of the argument conversions is checked.
204 using Tup = std::tuple<NothrowMoveable, int>;
205 Tup t;
206 ASSERT_NOT_NOEXCEPT(std::apply(f&: nec, t&: t));
207#if TEST_STD_VER >= 23
208 ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));
209#else
210 LIBCPP_ASSERT_NOEXCEPT(std::apply(f&: nec, t: std::move(t)));
211#endif
212 }
213}
214
215namespace ReturnTypeTest {
216 static int my_int = 42;
217
218 template <int N> struct index {};
219
220 void f(index<0>) {}
221
222 int f(index<1>) { return 0; }
223
224 int & f(index<2>) { return static_cast<int &>(my_int); }
225 int const & f(index<3>) { return static_cast<int const &>(my_int); }
226 int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }
227 int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }
228
229 int && f(index<6>) { return static_cast<int &&>(my_int); }
230 int const && f(index<7>) { return static_cast<int const &&>(my_int); }
231 int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }
232 int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }
233
234 int * f(index<10>) { return static_cast<int *>(&my_int); }
235 int const * f(index<11>) { return static_cast<int const *>(&my_int); }
236 int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }
237 int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }
238
239 template <int Func, class Expect>
240 void test()
241 {
242 using RawInvokeResult = decltype(f(index<Func>{}));
243 static_assert(std::is_same<RawInvokeResult, Expect>::value, "");
244 using FnType = RawInvokeResult (*) (index<Func>);
245 FnType fn = f;
246 std::tuple<index<Func>> t; ((void)t);
247 using InvokeResult = decltype(std::apply(fn, t));
248 static_assert(std::is_same<InvokeResult, Expect>::value, "");
249 }
250} // end namespace ReturnTypeTest
251
252void test_return_type()
253{
254 using ReturnTypeTest::test;
255 test<0, void>();
256 test<1, int>();
257 test<2, int &>();
258 test<3, int const &>();
259 test<4, int volatile &>();
260 test<5, int const volatile &>();
261 test<6, int &&>();
262 test<7, int const &&>();
263 test<8, int volatile &&>();
264 test<9, int const volatile &&>();
265 test<10, int *>();
266 test<11, int const *>();
267 test<12, int volatile *>();
268 test<13, int const volatile *>();
269}
270
271int main(int, char**) {
272 test_constexpr_evaluation();
273 test_call_quals_and_arg_types();
274 test_return_type();
275 test_noexcept();
276
277 return 0;
278}
279

source code of libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp