Skip to content

Commit 0a0f2e2

Browse files
committed
[libc++][functional] Test bind_front<NTTP>
1 parent 44e4880 commit 0a0f2e2

File tree

4 files changed

+443
-0
lines changed

4 files changed

+443
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
// REQUIRES: std-at-least-c++26
10+
11+
// <functional>
12+
13+
// Test the libc++ extension that std::bind_front<NTTP> is marked as [[nodiscard]].
14+
15+
#include <functional>
16+
17+
void test() {
18+
std::bind_front<test>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
19+
}
Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
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+
// REQUIRES: std-at-least-c++26
10+
11+
// <functional>
12+
13+
// template<auto f, class... Args>
14+
// constexpr unspecified bind_front(Args&&...);
15+
16+
#include <functional>
17+
18+
#include <cassert>
19+
#include <concepts>
20+
#include <tuple>
21+
#include <type_traits>
22+
#include <utility>
23+
24+
#include "types.h"
25+
26+
constexpr void test_basic_bindings() {
27+
{ // Bind arguments, call without arguments
28+
{
29+
auto f = std::bind_front<MakeTuple{}>();
30+
assert(f() == std::make_tuple());
31+
}
32+
{
33+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
34+
assert(f() == std::make_tuple(Elem<1>{}));
35+
}
36+
{
37+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
38+
assert(f() == std::make_tuple(Elem<1>{}, Elem<2>{}));
39+
}
40+
{
41+
auto f = std::bind_front<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_front<MakeTuple{}>();
49+
assert(f(Elem<1>{}) == std::make_tuple(Elem<1>{}));
50+
}
51+
{
52+
auto f = std::bind_front<MakeTuple{}>();
53+
assert(f(Elem<1>{}, Elem<2>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}));
54+
}
55+
{
56+
auto f = std::bind_front<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_front<MakeTuple{}>(Elem<1>{});
64+
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}));
65+
}
66+
{
67+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
68+
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}));
69+
}
70+
{
71+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
72+
assert(f(Elem<10>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}));
73+
}
74+
75+
{
76+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{});
77+
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<10>{}, Elem<11>{}));
78+
}
79+
{
80+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{});
81+
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<10>{}, Elem<11>{}));
82+
}
83+
{
84+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
85+
assert(f(Elem<10>{}, Elem<11>{}) == std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}));
86+
}
87+
{
88+
auto f = std::bind_front<MakeTuple{}>(Elem<1>{}, Elem<2>{}, Elem<3>{});
89+
assert(f(Elem<10>{}, Elem<11>{}, Elem<12>{}) ==
90+
std::make_tuple(Elem<1>{}, Elem<2>{}, Elem<3>{}, Elem<10>{}, Elem<11>{}, Elem<12>{}));
91+
}
92+
}
93+
94+
{ // Basic tests with fundamental types
95+
const int n = 2;
96+
const int m = 1;
97+
int o = 0;
98+
99+
auto add = [](int x, int y) { return x + y; };
100+
auto add6 = [](int a, int b, int c, int d, int e, int f) { return a + b + c + d + e + f; };
101+
auto increment = [](int& x) { return ++x; };
102+
103+
auto a = std::bind_front<add>(m, n);
104+
assert(a() == 3);
105+
106+
auto b = std::bind_front<add6>(m, n, m, m, m, m);
107+
assert(b() == 7);
108+
109+
auto c = std::bind_front<add6>(n, m);
110+
assert(c(1, 1, 1, 1) == 7);
111+
112+
auto f = std::bind_front<add>(n);
113+
assert(f(3) == 5);
114+
115+
auto g = std::bind_front<add>(n, 1);
116+
assert(g() == 3);
117+
118+
auto h = std::bind_front<add6>(1, 1, 1);
119+
assert(h(2, 2, 2) == 9);
120+
121+
auto i = std::bind_front<increment>();
122+
assert(i(o) == 1);
123+
assert(o == 1);
124+
125+
auto j = std::bind_front<increment>(std::ref(o));
126+
assert(j() == 2);
127+
assert(o == 2);
128+
}
129+
}
130+
131+
constexpr void test_edge_cases() {
132+
{ // Make sure we don't treat std::reference_wrapper specially.
133+
auto sub = [](std::reference_wrapper<int> a, std::reference_wrapper<int> b) { return a.get() - b.get(); };
134+
135+
int i = 1;
136+
int j = 2;
137+
auto f = std::bind_front<sub>(std::ref(i));
138+
assert(f(std::ref(j)) == -1);
139+
}
140+
141+
{ // Make sure we can call a function that's a pointer to a member function.
142+
struct MemberFunction {
143+
constexpr int mul(int x, int y) { return x * y; }
144+
};
145+
146+
MemberFunction value;
147+
auto fn = std::bind_front<&MemberFunction::mul>(value, 2);
148+
assert(fn(3) == 6);
149+
}
150+
151+
{ // Make sure we can call a function that's a pointer to a member object.
152+
struct MemberObject {
153+
int obj;
154+
};
155+
156+
MemberObject value{.obj = 3};
157+
auto fn1 = std::bind_front<&MemberObject::obj>();
158+
assert(fn1(value) == 3);
159+
auto fn2 = std::bind_front<&MemberObject::obj>(value);
160+
assert(fn2() == 3);
161+
}
162+
}
163+
164+
constexpr void test_passing_arguments() {
165+
{ // Make sure that we copy the bound arguments into the unspecified-type.
166+
int n = 2;
167+
auto f = std::bind_front<[](int x, int y) { return x + y; }>(n, 1);
168+
n = 100;
169+
assert(f() == 3);
170+
}
171+
172+
{ // Make sure we pass the bound arguments to the function object
173+
// with the right value category.
174+
{
175+
auto was_copied = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::copy; };
176+
CopyMoveInfo info;
177+
auto f = std::bind_front<was_copied>(info);
178+
assert(f());
179+
}
180+
181+
{
182+
auto was_moved = [](CopyMoveInfo info) { return info.copy_kind == CopyMoveInfo::move; };
183+
CopyMoveInfo info;
184+
auto f = std::bind_front<was_moved>(info);
185+
assert(std::move(f)());
186+
}
187+
}
188+
}
189+
190+
constexpr void test_perfect_forwarding_call_wrapper() {
191+
{ // Make sure we call the correctly cv-ref qualified operator()
192+
// based on the value category of the bind_front<NTTP> unspecified-type.
193+
struct X {
194+
constexpr int operator()() & { return 1; }
195+
constexpr int operator()() const& { return 2; }
196+
constexpr int operator()() && { return 3; }
197+
constexpr int operator()() const&& { return 4; }
198+
};
199+
200+
auto f = std::bind_front<X{}>();
201+
using F = decltype(f);
202+
assert(static_cast<F&>(f)() == 2);
203+
assert(static_cast<const F&>(f)() == 2);
204+
assert(static_cast<F&&>(f)() == 2);
205+
assert(static_cast<const F&&>(f)() == 2);
206+
}
207+
208+
// Call to `bind_front<NTTP>` unspecified-type's operator() should always result in call to the const& overload of the underlying function object.
209+
{
210+
{ // Make sure unspecified-type is still callable when we delete the & overload.
211+
struct X {
212+
int operator()() & = delete;
213+
int operator()() const&;
214+
int operator()() &&;
215+
int operator()() const&&;
216+
};
217+
218+
using F = decltype(std::bind_front<X{}>());
219+
static_assert(std::invocable<F&>);
220+
static_assert(std::invocable<const F&>);
221+
static_assert(std::invocable<F>);
222+
static_assert(std::invocable<const F>);
223+
}
224+
225+
{ // Make sure unspecified-type is not callable when we delete the const& overload.
226+
struct X {
227+
int operator()() &;
228+
int operator()() const& = delete;
229+
int operator()() &&;
230+
int operator()() const&&;
231+
};
232+
233+
using F = decltype(std::bind_front<X{}>());
234+
static_assert(!std::invocable<F&>);
235+
static_assert(!std::invocable<const F&>);
236+
static_assert(!std::invocable<F>);
237+
static_assert(!std::invocable<const F>);
238+
}
239+
240+
{ // Make sure unspecified-type is still callable when we delete the && overload.
241+
struct X {
242+
int operator()() &;
243+
int operator()() const&;
244+
int operator()() && = delete;
245+
int operator()() const&&;
246+
};
247+
248+
using F = decltype(std::bind_front<X{}>());
249+
static_assert(std::invocable<F&>);
250+
static_assert(std::invocable<const F&>);
251+
static_assert(std::invocable<F>);
252+
static_assert(std::invocable<const F>);
253+
}
254+
255+
{ // Make sure unspecified-type is still callable when we delete the const&& overload.
256+
struct X {
257+
int operator()() &;
258+
int operator()() const&;
259+
int operator()() &&;
260+
int operator()() const&& = delete;
261+
};
262+
263+
using F = decltype(std::bind_front<X{}>());
264+
static_assert(std::invocable<F&>);
265+
static_assert(std::invocable<const F&>);
266+
static_assert(std::invocable<F>);
267+
static_assert(std::invocable<const F>);
268+
}
269+
}
270+
271+
{ // Test perfect forwarding
272+
auto f = [](int& val) {
273+
val = 5;
274+
return 10;
275+
};
276+
277+
auto bf = std::bind_front<f>();
278+
int val = 0;
279+
assert(bf(val) == 10);
280+
assert(val == 5);
281+
282+
using BF = decltype(bf);
283+
static_assert(std::invocable<BF, int&>);
284+
static_assert(!std::invocable<BF, int>);
285+
}
286+
}
287+
288+
constexpr void test_return_type() {
289+
{ // Test constructors and assignment operators
290+
struct LeftShift {
291+
constexpr unsigned int operator()(unsigned int x, unsigned int y) const { return x << y; }
292+
};
293+
294+
auto power_of_2 = std::bind_front<LeftShift{}>(1);
295+
assert(power_of_2(5) == 32U);
296+
assert(power_of_2(4) == 16U);
297+
298+
auto moved = std::move(power_of_2);
299+
assert(moved(6) == 64);
300+
assert(moved(7) == 128);
301+
302+
auto copied = power_of_2;
303+
assert(copied(3) == 8);
304+
assert(copied(2) == 4);
305+
306+
moved = std::move(copied);
307+
assert(copied(1) == 2);
308+
assert(copied(0) == 1);
309+
310+
copied = moved;
311+
assert(copied(8) == 256);
312+
assert(copied(9) == 512);
313+
}
314+
315+
{ // Make sure `bind_front<NTTP>` unspecified-type's operator() is SFINAE-friendly.
316+
using F = decltype(std::bind_front<[](int x, int y) { return x / y; }>(1));
317+
static_assert(!std::is_invocable<F>::value);
318+
static_assert(std::is_invocable<F, int>::value);
319+
static_assert(!std::is_invocable<F, void*>::value);
320+
static_assert(!std::is_invocable<F, int, int>::value);
321+
}
322+
323+
{ // Test noexceptness
324+
auto always_noexcept = std::bind_front<MaybeNoexceptFn<true>{}>();
325+
static_assert(noexcept(always_noexcept()));
326+
327+
auto never_noexcept = std::bind_front<MaybeNoexceptFn<false>{}>();
328+
static_assert(!noexcept(never_noexcept()));
329+
}
330+
331+
{ // Test calling volatile wrapper
332+
using Fn = decltype(std::bind_front<std::integral_constant<int, 0>{}>());
333+
static_assert(!std::invocable<volatile Fn&>);
334+
static_assert(!std::invocable<const volatile Fn&>);
335+
static_assert(!std::invocable<volatile Fn>);
336+
static_assert(!std::invocable<const volatile Fn>);
337+
}
338+
}
339+
340+
constexpr bool test() {
341+
test_basic_bindings();
342+
test_edge_cases();
343+
test_passing_arguments();
344+
test_perfect_forwarding_call_wrapper();
345+
test_return_type();
346+
347+
return true;
348+
}
349+
350+
int main(int, char**) {
351+
test();
352+
static_assert((test(), true));
353+
354+
return 0;
355+
}

0 commit comments

Comments
 (0)