1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/functional/Invoke.h>
18
19 #include <folly/CppAttributes.h>
20 #include <folly/portability/GTest.h>
21
22 class InvokeTest : public testing::Test {};
23
24 namespace {
25
26 struct from_any {
27 template <typename T>
from_any__anonf18d853e0111::from_any28 /* implicit */ from_any(T&&) {}
29 };
30
31 struct Fn {
operator ()__anonf18d853e0111::Fn32 char operator()(int, int) noexcept { return 'a'; }
operator ()__anonf18d853e0111::Fn33 int volatile&& operator()(int, char const*) { return std::move(x_); }
operator ()__anonf18d853e0111::Fn34 float operator()(float, float) { return 3.14; }
35 int volatile x_ = 17;
36 };
37
38 namespace invoker {
39
40 FOLLY_CREATE_MEMBER_INVOKER_SUITE(test);
41
42 }
43
44 struct Obj {
test__anonf18d853e0111::Obj45 char test(int, int) noexcept { return 'a'; }
test__anonf18d853e0111::Obj46 int volatile&& test(int, char const*) { return std::move(x_); }
test__anonf18d853e0111::Obj47 float test(float, float) { return 3.14; }
48 int volatile x_ = 17;
49 };
50
51 namespace x {
52 struct Obj {};
go(Obj const &,int)53 int go(Obj const&, int) noexcept {
54 return 3;
55 }
56 } // namespace x
57
58 namespace y {
59 struct Obj {};
go(Obj const &,char const *)60 char go(Obj const&, char const*) {
61 return 'a';
62 }
63 } // namespace y
64
65 namespace z {
66 struct Obj {};
67 } // namespace z
go(z::Obj const &,int)68 FOLLY_MAYBE_UNUSED float go(z::Obj const&, int) {
69 return 9;
70 }
71
72 namespace swappable {
73 struct Obj {
74 int x_;
75 };
swap(Obj &,Obj &)76 void swap(Obj&, Obj&) noexcept {} // no-op
77 } // namespace swappable
78
79 struct AltSwappable {};
80 struct AltSwappableRet {};
81 namespace unswappable {
82 FOLLY_MAYBE_UNUSED AltSwappableRet swap(AltSwappable&, AltSwappable&);
83 } // namespace unswappable
84
85 namespace invoker {
86
87 FOLLY_CREATE_FREE_INVOKER_SUITE(go);
88 FOLLY_CREATE_FREE_INVOKER_SUITE(swap, std, unswappable);
89 FOLLY_CREATE_FREE_INVOKER_SUITE(no_such_thing);
90 FOLLY_CREATE_QUAL_INVOKER_SUITE(std_swap, ::std::swap);
91
92 } // namespace invoker
93
94 } // namespace
95
TEST_F(InvokeTest,invoke)96 TEST_F(InvokeTest, invoke) {
97 Fn fn;
98
99 EXPECT_TRUE(noexcept(folly::invoke(fn, 1, 2)));
100 EXPECT_FALSE(noexcept(folly::invoke(fn, 1, "2")));
101
102 EXPECT_EQ('a', folly::invoke(fn, 1, 2));
103 EXPECT_EQ(17, folly::invoke(fn, 1, "2"));
104
105 using FnA = char (Fn::*)(int, int);
106 using FnB = int volatile && (Fn::*)(int, char const*);
107 EXPECT_EQ('a', folly::invoke(static_cast<FnA>(&Fn::operator()), fn, 1, 2));
108 EXPECT_EQ(17, folly::invoke(static_cast<FnB>(&Fn::operator()), fn, 1, "2"));
109 }
110
TEST_F(InvokeTest,invoke_result)111 TEST_F(InvokeTest, invoke_result) {
112 EXPECT_TRUE(
113 (std::is_same<char, folly::invoke_result_t<Fn, int, char>>::value));
114 EXPECT_TRUE(
115 (std::is_same<int volatile&&, folly::invoke_result_t<Fn, int, char*>>::
116 value));
117 }
118
TEST_F(InvokeTest,is_invocable)119 TEST_F(InvokeTest, is_invocable) {
120 EXPECT_TRUE((folly::is_invocable_v<Fn, int, char>));
121 EXPECT_TRUE((folly::is_invocable_v<Fn, int, char*>));
122 EXPECT_FALSE((folly::is_invocable_v<Fn, int>));
123 }
124
TEST_F(InvokeTest,is_invocable_r)125 TEST_F(InvokeTest, is_invocable_r) {
126 EXPECT_TRUE((folly::is_invocable_r_v<int, Fn, int, char>));
127 EXPECT_TRUE((folly::is_invocable_r_v<int, Fn, int, char*>));
128 EXPECT_FALSE((folly::is_invocable_r_v<int, Fn, int>));
129 }
130
TEST_F(InvokeTest,is_nothrow_invocable)131 TEST_F(InvokeTest, is_nothrow_invocable) {
132 EXPECT_TRUE((folly::is_nothrow_invocable_v<Fn, int, char>));
133 EXPECT_FALSE((folly::is_nothrow_invocable_v<Fn, int, char*>));
134 EXPECT_FALSE((folly::is_nothrow_invocable_v<Fn, int>));
135 }
136
TEST_F(InvokeTest,is_nothrow_invocable_r)137 TEST_F(InvokeTest, is_nothrow_invocable_r) {
138 EXPECT_TRUE((folly::is_nothrow_invocable_r_v<int, Fn, int, char>));
139 EXPECT_FALSE((folly::is_nothrow_invocable_r_v<int, Fn, int, char*>));
140 EXPECT_FALSE((folly::is_nothrow_invocable_r_v<int, Fn, int>));
141 }
142
TEST_F(InvokeTest,free_invoke)143 TEST_F(InvokeTest, free_invoke) {
144 using traits = folly::invoke_traits<decltype(invoker::go)>;
145
146 x::Obj x_;
147 y::Obj y_;
148
149 EXPECT_TRUE(noexcept(traits::invoke(x_, 3)));
150 EXPECT_FALSE(noexcept(traits::invoke(y_, "hello")));
151
152 EXPECT_EQ(3, traits::invoke(x_, 3));
153 EXPECT_EQ('a', traits::invoke(y_, "hello"));
154 }
155
TEST_F(InvokeTest,free_invoke_result)156 TEST_F(InvokeTest, free_invoke_result) {
157 using traits = folly::invoke_traits<decltype(invoker::go)>;
158
159 EXPECT_TRUE((std::is_same<int, traits::invoke_result_t<x::Obj, int>>::value));
160 EXPECT_TRUE((
161 std::is_same<char, traits::invoke_result_t<y::Obj, char const*>>::value));
162 }
163
TEST_F(InvokeTest,free_is_invocable)164 TEST_F(InvokeTest, free_is_invocable) {
165 using traits = folly::invoke_traits<decltype(invoker::go)>;
166
167 EXPECT_TRUE((traits::is_invocable_v<x::Obj, int>));
168 EXPECT_TRUE((traits::is_invocable_v<y::Obj, char const*>));
169 EXPECT_FALSE((traits::is_invocable_v<z::Obj, int>));
170 EXPECT_FALSE((traits::is_invocable_v<float>));
171 }
172
TEST_F(InvokeTest,free_is_invocable_r)173 TEST_F(InvokeTest, free_is_invocable_r) {
174 using traits = folly::invoke_traits<decltype(invoker::go)>;
175
176 EXPECT_TRUE((traits::is_invocable_r_v<int, x::Obj, int>));
177 EXPECT_TRUE((traits::is_invocable_r_v<char, y::Obj, char const*>));
178 EXPECT_FALSE((traits::is_invocable_r_v<float, z::Obj, int>));
179 EXPECT_FALSE((traits::is_invocable_r_v<from_any, float>));
180 }
181
TEST_F(InvokeTest,free_is_nothrow_invocable)182 TEST_F(InvokeTest, free_is_nothrow_invocable) {
183 using traits = folly::invoke_traits<decltype(invoker::go)>;
184
185 EXPECT_TRUE((traits::is_nothrow_invocable_v<x::Obj, int>));
186 EXPECT_FALSE((traits::is_nothrow_invocable_v<y::Obj, char const*>));
187 EXPECT_FALSE((traits::is_nothrow_invocable_v<z::Obj, int>));
188 EXPECT_FALSE((traits::is_nothrow_invocable_v<float>));
189 }
190
TEST_F(InvokeTest,free_is_nothrow_invocable_r)191 TEST_F(InvokeTest, free_is_nothrow_invocable_r) {
192 using traits = folly::invoke_traits<decltype(invoker::go)>;
193
194 EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, x::Obj, int>));
195 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<char, y::Obj, char const*>));
196 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<float, z::Obj, int>));
197 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<from_any, float>));
198 }
199
TEST_F(InvokeTest,free_invoke_swap)200 TEST_F(InvokeTest, free_invoke_swap) {
201 using traits = folly::invoke_traits<decltype(invoker::swap)>;
202
203 int a = 3;
204 int b = 4;
205
206 traits::invoke(a, b);
207 EXPECT_EQ(4, a);
208 EXPECT_EQ(3, b);
209
210 swappable::Obj x{3};
211 swappable::Obj y{4};
212
213 traits::invoke(x, y);
214 EXPECT_EQ(3, x.x_);
215 EXPECT_EQ(4, y.x_);
216
217 std::swap(x, y);
218 EXPECT_EQ(4, x.x_);
219 EXPECT_EQ(3, y.x_);
220
221 EXPECT_TRUE((
222 traits::is_invocable_r_v<AltSwappableRet, AltSwappable&, AltSwappable&>));
223 }
224
TEST_F(InvokeTest,qual_invoke_swap)225 TEST_F(InvokeTest, qual_invoke_swap) {
226 using traits = folly::invoke_traits<decltype(invoker::std_swap)>;
227
228 int a = 3;
229 int b = 4;
230
231 traits::invoke(a, b);
232 EXPECT_EQ(4, a);
233 EXPECT_EQ(3, b);
234
235 swappable::Obj x{3};
236 swappable::Obj y{4};
237
238 traits::invoke(x, y);
239 EXPECT_EQ(4, x.x_);
240 EXPECT_EQ(3, y.x_);
241
242 std::swap(x, y);
243 EXPECT_EQ(3, x.x_);
244 EXPECT_EQ(4, y.x_);
245 }
246
TEST_F(InvokeTest,invoke_qual)247 TEST_F(InvokeTest, invoke_qual) {
248 auto go = FOLLY_INVOKE_QUAL(::std::swap);
249
250 int a = 3;
251 int b = 4;
252 EXPECT_TRUE(noexcept(go(a, b)));
253
254 go(a, b);
255 EXPECT_EQ(4, a);
256 EXPECT_EQ(3, b);
257 }
258
TEST_F(InvokeTest,member_invoke)259 TEST_F(InvokeTest, member_invoke) {
260 using traits = folly::invoke_traits<decltype(invoker::test)>;
261
262 Obj fn;
263
264 EXPECT_TRUE(noexcept(traits::invoke(fn, 1, 2)));
265 EXPECT_FALSE(noexcept(traits::invoke(fn, 1, "2")));
266
267 EXPECT_EQ('a', traits::invoke(fn, 1, 2));
268 EXPECT_EQ(17, traits::invoke(fn, 1, "2"));
269 }
270
TEST_F(InvokeTest,member_invoke_result)271 TEST_F(InvokeTest, member_invoke_result) {
272 using traits = folly::invoke_traits<invoker::test_fn>;
273
274 EXPECT_TRUE(
275 (std::is_same<char, traits::invoke_result_t<Obj, int, char>>::value));
276 EXPECT_TRUE(
277 (std::is_same<int volatile&&, traits::invoke_result_t<Obj, int, char*>>::
278 value));
279 }
280
TEST_F(InvokeTest,member_is_invocable)281 TEST_F(InvokeTest, member_is_invocable) {
282 using traits = folly::invoke_traits<decltype(invoker::test)>;
283
284 EXPECT_TRUE((traits::is_invocable_v<Obj, int, char>));
285 EXPECT_TRUE((traits::is_invocable_v<Obj, int, char*>));
286 EXPECT_FALSE((traits::is_invocable_v<Obj, int>));
287 }
288
TEST_F(InvokeTest,member_is_invocable_r)289 TEST_F(InvokeTest, member_is_invocable_r) {
290 using traits = folly::invoke_traits<decltype(invoker::test)>;
291
292 EXPECT_TRUE((traits::is_invocable_r_v<int, Obj, int, char>));
293 EXPECT_TRUE((traits::is_invocable_r_v<int, Obj, int, char*>));
294 EXPECT_FALSE((traits::is_invocable_r_v<int, Obj, int>));
295 }
296
TEST_F(InvokeTest,member_is_nothrow_invocable)297 TEST_F(InvokeTest, member_is_nothrow_invocable) {
298 using traits = folly::invoke_traits<decltype(invoker::test)>;
299
300 EXPECT_TRUE((traits::is_nothrow_invocable_v<Obj, int, char>));
301 EXPECT_FALSE((traits::is_nothrow_invocable_v<Obj, int, char*>));
302 EXPECT_FALSE((traits::is_nothrow_invocable_v<Obj, int>));
303 }
304
TEST_F(InvokeTest,member_is_nothrow_invocable_r)305 TEST_F(InvokeTest, member_is_nothrow_invocable_r) {
306 using traits = folly::invoke_traits<decltype(invoker::test)>;
307
308 EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, Obj, int, char>));
309 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, Obj, int, char*>));
310 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, Obj, int>));
311 }
312
TEST_F(InvokeTest,invoke_member)313 TEST_F(InvokeTest, invoke_member) {
314 auto test = FOLLY_INVOKE_MEMBER(test);
315
316 Obj fn;
317
318 EXPECT_TRUE(noexcept(test(fn, 1, 2)));
319 EXPECT_FALSE(noexcept(test(fn, 1, "2")));
320
321 EXPECT_EQ('a', test(fn, 1, 2));
322 EXPECT_EQ(17, test(fn, 1, "2"));
323 }
324
325 namespace {
326
327 namespace invoker {
328
329 FOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE(stat);
330
331 }
332
333 } // namespace
334
TEST_F(InvokeTest,static_member_invoke)335 TEST_F(InvokeTest, static_member_invoke) {
336 struct HasStat {
337 static char stat(int, int) noexcept { return 'a'; }
338 static int volatile&& stat(int, char const*) {
339 static int volatile x_ = 17;
340 return std::move(x_);
341 }
342 static float stat(float, float) { return 3.14; }
343 };
344 using traits = folly::invoke_traits<decltype(invoker::stat<HasStat>)>;
345
346 EXPECT_TRUE((traits::is_invocable_v<int, char>));
347 EXPECT_TRUE((traits::is_invocable_v<int, char>));
348 EXPECT_TRUE((traits::is_invocable_v<int, char*>));
349 EXPECT_FALSE((traits::is_invocable_v<int>));
350
351 EXPECT_TRUE((traits::is_invocable_r_v<int, int, char>));
352 EXPECT_TRUE((traits::is_invocable_r_v<int, int, char*>));
353 EXPECT_FALSE((traits::is_invocable_r_v<int, int>));
354
355 EXPECT_TRUE((traits::is_nothrow_invocable_v<int, char>));
356 EXPECT_FALSE((traits::is_nothrow_invocable_v<int, char*>));
357 EXPECT_FALSE((traits::is_nothrow_invocable_v<int>));
358
359 EXPECT_TRUE((traits::is_nothrow_invocable_r_v<int, int, char>));
360 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int, char*>));
361 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int>));
362 }
363
TEST_F(InvokeTest,static_member_no_invoke)364 TEST_F(InvokeTest, static_member_no_invoke) {
365 struct HasNoStat {};
366
367 using traits = folly::invoke_traits<decltype(invoker::stat<HasNoStat>)>;
368
369 EXPECT_FALSE((traits::is_invocable_v<>));
370 EXPECT_FALSE((traits::is_invocable_v<int>));
371
372 EXPECT_FALSE((traits::is_invocable_r_v<int>));
373 EXPECT_FALSE((traits::is_invocable_r_v<int, int>));
374
375 EXPECT_FALSE((traits::is_nothrow_invocable_v<>));
376 EXPECT_FALSE((traits::is_nothrow_invocable_v<int>));
377
378 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int>));
379 EXPECT_FALSE((traits::is_nothrow_invocable_r_v<int, int>));
380 }
381
382 namespace {
383
384 struct TestCustomisationPointFn {
385 template <typename T, typename U>
operator ()__anonf18d853e0311::TestCustomisationPointFn386 constexpr auto operator()(T&& t, U&& u) const noexcept(
387 folly::is_nothrow_tag_invocable_v<TestCustomisationPointFn, T, U>)
388 -> folly::tag_invoke_result_t<TestCustomisationPointFn, T, U> {
389 return folly::tag_invoke(*this, static_cast<T&&>(t), static_cast<U&&>(u));
390 }
391 };
392
393 FOLLY_DEFINE_CPO(TestCustomisationPointFn, testCustomisationPoint)
394
395 struct TypeA {
tag_invoke(folly::cpo_t<testCustomisationPoint>,const TypeA &,int value)396 constexpr friend int tag_invoke(
397 folly::cpo_t<testCustomisationPoint>, const TypeA&, int value) {
398 return value * 2;
399 }
tag_invoke(folly::cpo_t<testCustomisationPoint>,const TypeA &,bool value)400 constexpr friend bool tag_invoke(
401 folly::cpo_t<testCustomisationPoint>, const TypeA&, bool value) noexcept {
402 return !value;
403 }
404 };
405
406 } // namespace
407
408 static_assert(
409 folly::is_invocable<decltype(testCustomisationPoint), TypeA, int>::value);
410 static_assert(
411 !folly::is_invocable<decltype(testCustomisationPoint), TypeA, TypeA>::
412 value);
413 static_assert(
414 folly::is_nothrow_invocable<decltype(testCustomisationPoint), TypeA, bool>::
415 value);
416 static_assert(
417 !folly::is_nothrow_invocable<decltype(testCustomisationPoint), TypeA, int>::
418 value);
419 static_assert(
420 std::is_same<
421 folly::invoke_result_t<decltype(testCustomisationPoint), TypeA, int>,
422 int>::value);
423
424 // Test that the CPO forwards through constexpr-ness of the
425 // customisations by evaluating the CPO in a static_assert()
426 // which forces compile-time evaluation.
427 static_assert(testCustomisationPoint(TypeA{}, 10) == 20);
428 static_assert(!testCustomisationPoint(TypeA{}, true));
429
TEST_F(InvokeTest,TagInvokeCustomisationPoint)430 TEST_F(InvokeTest, TagInvokeCustomisationPoint) {
431 const TypeA a;
432 EXPECT_EQ(10, testCustomisationPoint(a, 5));
433 EXPECT_EQ(false, testCustomisationPoint(a, true));
434 }
435