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 | // <functional> |
12 | |
13 | // template<class F, class... Args> |
14 | // constexpr unspecified bind_back(F&& f, Args&&... args); |
15 | |
16 | #include <functional> |
17 | |
18 | #include <cassert> |
19 | #include <concepts> |
20 | #include <tuple> |
21 | #include <utility> |
22 | |
23 | #include "callable_types.h" |
24 | #include "types.h" |
25 | |
26 | constexpr void test_basic_bindings() { |
27 | { // Bind arguments, call without arguments |
28 | { |
29 | auto f = std::bind_back(MakeTuple{}); |
30 | assert(f() == std::make_tuple()); |
31 | } |
32 | { |
33 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}); |
34 | assert(f() == std::make_tuple(Elem<1>{})); |
35 | } |
36 | { |
37 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); |
38 | assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{})); |
39 | } |
40 | { |
41 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); |
42 | assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{})); |
43 | } |
44 | } |
45 | |
46 | { // Bind no arguments, call with arguments |
47 | { |
48 | auto f = std::bind_back(MakeTuple{}); |
49 | assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{})); |
50 | } |
51 | { |
52 | auto f = std::bind_back(MakeTuple{}); |
53 | assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{})); |
54 | } |
55 | { |
56 | auto f = std::bind_back(MakeTuple{}); |
57 | assert(f(Elem<1>{}, Elem<2>{}, Elem<3>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{})); |
58 | } |
59 | } |
60 | |
61 | { // Bind arguments, call with arguments |
62 | { |
63 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}); |
64 | assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{})); |
65 | } |
66 | { |
67 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); |
68 | assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{})); |
69 | } |
70 | { |
71 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); |
72 | assert(f(Elem<10>{}) == std::make_tuple(Elem<10>{}, Elem<1>{}, Elem<2>{}, Elem<3>{})); |
73 | } |
74 | |
75 | { |
76 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}); |
77 | assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{})); |
78 | } |
79 | { |
80 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}); |
81 | assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{})); |
82 | } |
83 | { |
84 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); |
85 | assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<1>{}, Elem<2>{}, Elem<3>{})); |
86 | } |
87 | { |
88 | auto f = std::bind_back(MakeTuple{}, Elem<1>{}, Elem<2>{}, Elem<3>{}); |
89 | assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) == |
90 | std::make_tuple(Elem<10>{}, Elem<11>{}, Elem<12>{}, Elem<1>{}, Elem<2>{}, Elem<3>{})); |
91 | } |
92 | } |
93 | |
94 | { // Basic tests with fundamental types |
95 | int n = 2; |
96 | int m = 1; |
97 | int sum = 0; |
98 | auto add = [](int x, int y) { return x + y; }; |
99 | auto add_n = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; }; |
100 | auto add_ref = [&](int x, int y) -> int& { return sum = x + y; }; |
101 | auto add_rref = [&](int x, int y) -> int&& { return std::move(sum = x + y); }; |
102 | |
103 | auto a = std::bind_back(add, m, n); |
104 | assert(a() == 3); |
105 | |
106 | auto b = std::bind_back(add_n, m, n, m, m, m, m); |
107 | assert(b() == 7); |
108 | |
109 | auto c = std::bind_back(add_n, n, m); |
110 | assert(c(1, 1, 1, 1) == 7); |
111 | |
112 | auto d = std::bind_back(add_ref, n, m); |
113 | std::same_as<int&> decltype(auto) dresult(d()); |
114 | assert(dresult == 3); |
115 | |
116 | auto e = std::bind_back(add_rref, n, m); |
117 | std::same_as<int&&> decltype(auto) eresult(e()); |
118 | assert(eresult == 3); |
119 | |
120 | auto f = std::bind_back(add, n); |
121 | assert(f(3) == 5); |
122 | |
123 | auto g = std::bind_back(add, n, 1); |
124 | assert(g() == 3); |
125 | |
126 | auto h = std::bind_back(add_n, 1, 1, 1); |
127 | assert(h(2, 2, 2) == 9); |
128 | |
129 | auto i = std::bind_back(add_ref, n); |
130 | std::same_as<int&> decltype(auto) iresult(i(5)); |
131 | assert(iresult == 7); |
132 | |
133 | auto j = std::bind_back(add_rref, m); |
134 | std::same_as<int&&> decltype(auto) jresult(j(4)); |
135 | assert(jresult == 5); |
136 | } |
137 | } |
138 | |
139 | constexpr void test_edge_cases() { |
140 | { // Make sure we don't treat std::reference_wrapper specially. |
141 | auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() - b.get(); }; |
142 | |
143 | int i = 1; |
144 | int j = 2; |
145 | auto f = std::bind_back(sub, std::ref(i)); |
146 | assert(f(std::ref(j)) == 1); |
147 | } |
148 | |
149 | { // Make sure we can call a function that's a pointer to a member function. |
150 | struct MemberFunction { |
151 | constexpr int foo(int x, int y) { return x * y; } |
152 | }; |
153 | |
154 | MemberFunction value; |
155 | auto fn = std::bind_back(&MemberFunction::foo, 2, 3); |
156 | assert(fn(value) == 6); |
157 | } |
158 | |
159 | { // Make sure we can call a function that's a pointer to a member object. |
160 | struct MemberObject { |
161 | int obj; |
162 | }; |
163 | |
164 | MemberObject value{.obj = 3}; |
165 | auto fn = std::bind_back(&MemberObject::obj); |
166 | assert(fn(value) == 3); |
167 | } |
168 | } |
169 | |
170 | constexpr void test_passing_arguments() { |
171 | { // Make sure that we copy the bound arguments into the unspecified-type. |
172 | auto add = [](int x, int y) { return x + y; }; |
173 | int n = 2; |
174 | auto f = std::bind_back(add, n, 1); |
175 | n = 100; |
176 | assert(f() == 3); |
177 | } |
178 | |
179 | { // Make sure we pass the bound arguments to the function object |
180 | // with the right value category. |
181 | { |
182 | auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; }; |
183 | CopyMoveInfo info; |
184 | auto f = std::bind_back(was_copied, info); |
185 | assert(f()); |
186 | } |
187 | |
188 | { |
189 | auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; }; |
190 | CopyMoveInfo info; |
191 | auto f = std::bind_back(was_moved, info); |
192 | assert(std::move(f)()); |
193 | } |
194 | } |
195 | } |
196 | |
197 | constexpr void test_function_objects() { |
198 | { // Make sure we call the correctly cv-ref qualified operator() |
199 | // based on the value category of the bind_back unspecified-type. |
200 | struct X { |
201 | constexpr int operator()() & { return 1; } |
202 | constexpr int operator()() const& { return 2; } |
203 | constexpr int operator()() && { return 3; } |
204 | constexpr int operator()() const&& { return 4; } |
205 | }; |
206 | |
207 | auto f = std::bind_back(X{}); |
208 | using F = decltype(f); |
209 | assert(static_cast<F&>(f)() == 1); |
210 | assert(static_cast<const F&>(f)() == 2); |
211 | assert(static_cast<F&&>(f)() == 3); |
212 | assert(static_cast<const F&&>(f)() == 4); |
213 | } |
214 | |
215 | // Make sure the `bind_back` unspecified-type does not model invocable |
216 | // when the call would select a differently-qualified operator(). |
217 | // |
218 | // For example, if the call to `operator()() &` is ill-formed, the call to the unspecified-type |
219 | // should be ill-formed and not fall back to the `operator()() const&` overload. |
220 | { // Make sure we delete the & overload when the underlying call isn't valid. |
221 | { |
222 | struct X { |
223 | void operator()() & = delete; |
224 | void operator()() const&; |
225 | void operator()() &&; |
226 | void operator()() const&&; |
227 | }; |
228 | |
229 | using F = decltype(std::bind_back(X{})); |
230 | static_assert(!std::invocable<F&>); |
231 | static_assert(std::invocable<const F&>); |
232 | static_assert(std::invocable<F>); |
233 | static_assert(std::invocable<const F>); |
234 | } |
235 | |
236 | // There's no way to make sure we delete the const& overload when the underlying call isn't valid, |
237 | // so we can't check this one. |
238 | |
239 | { // Make sure we delete the && overload when the underlying call isn't valid. |
240 | struct X { |
241 | void operator()() &; |
242 | void operator()() const&; |
243 | void operator()() && = delete; |
244 | void operator()() const&&; |
245 | }; |
246 | |
247 | using F = decltype(std::bind_back(X{})); |
248 | static_assert(std::invocable<F&>); |
249 | static_assert(std::invocable<const F&>); |
250 | static_assert(!std::invocable<F>); |
251 | static_assert(std::invocable<const F>); |
252 | } |
253 | |
254 | { // Make sure we delete the const&& overload when the underlying call isn't valid. |
255 | struct X { |
256 | void operator()() &; |
257 | void operator()() const&; |
258 | void operator()() &&; |
259 | void operator()() const&& = delete; |
260 | }; |
261 | |
262 | using F = decltype(std::bind_back(X{})); |
263 | static_assert(std::invocable<F&>); |
264 | static_assert(std::invocable<const F&>); |
265 | static_assert(std::invocable<F>); |
266 | static_assert(!std::invocable<const F>); |
267 | } |
268 | } |
269 | |
270 | { // Extra value category tests |
271 | struct X {}; |
272 | |
273 | { |
274 | struct Y { |
275 | void operator()(X&&) const&; |
276 | void operator()(X&&) && = delete; |
277 | }; |
278 | |
279 | using F = decltype(std::bind_back(Y{})); |
280 | static_assert(std::invocable<F&, X>); |
281 | static_assert(!std::invocable<F, X>); |
282 | } |
283 | |
284 | { |
285 | struct Y { |
286 | void operator()(const X&) const; |
287 | void operator()(X&&) const = delete; |
288 | }; |
289 | |
290 | using F = decltype(std::bind_back(Y{}, X{})); |
291 | static_assert(std::invocable<F&>); |
292 | static_assert(!std::invocable<F>); |
293 | } |
294 | } |
295 | } |
296 | |
297 | constexpr void test_return_type() { |
298 | { // Test properties of the constructor of the unspecified-type returned by bind_back. |
299 | { // Test move constructor when function is move only. |
300 | MoveOnlyCallable<bool> value(true); |
301 | auto f = std::bind_back(std::move(value), 1); |
302 | assert(f()); |
303 | assert(f(1, 2, 3)); |
304 | |
305 | auto f1 = std::move(f); |
306 | assert(!f()); |
307 | assert(f1()); |
308 | assert(f1(1, 2, 3)); |
309 | |
310 | using F = decltype(f); |
311 | static_assert(std::is_move_constructible<F>::value); |
312 | static_assert(!std::is_copy_constructible<F>::value); |
313 | static_assert(!std::is_move_assignable<F>::value); |
314 | static_assert(!std::is_copy_assignable<F>::value); |
315 | } |
316 | |
317 | { // Test move constructor when function is copyable but not assignable. |
318 | CopyCallable<bool> value(true); |
319 | auto f = std::bind_back(value, 1); |
320 | assert(f()); |
321 | assert(f(1, 2, 3)); |
322 | |
323 | auto f1 = std::move(f); |
324 | assert(!f()); |
325 | assert(f1()); |
326 | assert(f1(1, 2, 3)); |
327 | |
328 | auto f2 = std::bind_back(std::move(value), 1); |
329 | assert(f1()); |
330 | assert(f2()); |
331 | assert(f2(1, 2, 3)); |
332 | |
333 | using F = decltype(f); |
334 | static_assert(std::is_move_constructible<F>::value); |
335 | static_assert(std::is_copy_constructible<F>::value); |
336 | static_assert(!std::is_move_assignable<F>::value); |
337 | static_assert(!std::is_copy_assignable<F>::value); |
338 | } |
339 | |
340 | { // Test constructors when function is copy assignable. |
341 | using F = decltype(std::bind_back(std::declval<CopyAssignableWrapper&>(), 1)); |
342 | static_assert(std::is_move_constructible<F>::value); |
343 | static_assert(std::is_copy_constructible<F>::value); |
344 | static_assert(std::is_move_assignable<F>::value); |
345 | static_assert(std::is_copy_assignable<F>::value); |
346 | } |
347 | |
348 | { // Test constructors when function is move assignable only. |
349 | using F = decltype(std::bind_back(std::declval<MoveAssignableWrapper>(), 1)); |
350 | static_assert(std::is_move_constructible<F>::value); |
351 | static_assert(!std::is_copy_constructible<F>::value); |
352 | static_assert(std::is_move_assignable<F>::value); |
353 | static_assert(!std::is_copy_assignable<F>::value); |
354 | } |
355 | } |
356 | |
357 | { // Make sure bind_back's unspecified type's operator() is SFINAE-friendly. |
358 | using F = decltype(std::bind_back(std::declval<int (*)(int, int)>(), 1)); |
359 | static_assert(!std::is_invocable<F>::value); |
360 | static_assert(std::is_invocable<F, int>::value); |
361 | static_assert(!std::is_invocable<F, void*>::value); |
362 | static_assert(!std::is_invocable<F, int, int>::value); |
363 | } |
364 | } |
365 | |
366 | constexpr bool test() { |
367 | test_basic_bindings(); |
368 | test_edge_cases(); |
369 | test_passing_arguments(); |
370 | test_function_objects(); |
371 | test_return_type(); |
372 | |
373 | return true; |
374 | } |
375 | |
376 | int main(int, char**) { |
377 | test(); |
378 | static_assert(test()); |
379 | |
380 | return 0; |
381 | } |
382 | |