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