1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
2 // https://github.com/Dobiasd/FunctionalPlus
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 #include <doctest/doctest.h>
8 #include <functional>
9 #include <fplus/internal/invoke.hpp>
10
11 #include <initializer_list>
12 #include <stdexcept>
13 #include <string>
14 #include <tuple>
15
16 using namespace fplus;
17
18 #if defined(_MSC_VER) && (_MSC_VER < 1910)
19 // only one test fails to compile with MSVC 2015, all tests pass with MSVC 2017
20 #define FPLUS_MSVC2015_BYPASS_FAILING_TESTS
21 #endif
22
23 namespace
24 {
25 template <typename T>
26 struct identity
27 {
28 using type = T;
29 };
30
31 template <typename Ret, typename Class, typename... Args>
cv_qualifiers(identity<Ret (Class::*)(Args...)>)32 auto cv_qualifiers(identity<Ret (Class::*)(Args...)>) {
33 return std::tuple<identity<Ret (Class::*)(Args...)>,
34 identity<Ret (Class::*)(Args...) const>,
35 identity<Ret (Class::*)(Args...) volatile>,
36 identity<Ret (Class::*)(Args...) const volatile>>{};
37 }
38
39 template <typename Ret, typename Class, typename... Args>
ref_qualifiers(identity<Ret (Class::*)(Args...)>)40 auto ref_qualifiers(identity<Ret (Class::*)(Args...)>)
41 {
42 return std::tuple<identity<Ret (Class::*)(Args...)&>,
43 identity<Ret (Class::*)(Args...) &&>>{};
44 }
45
46 template <typename Ret, typename Class, typename... Args>
ref_qualifiers(identity<Ret (Class::*)(Args...)const>)47 auto ref_qualifiers(identity<Ret (Class::*)(Args...) const>)
48 {
49 return std::tuple<identity<Ret (Class::*)(Args...) const&>,
50 identity<Ret (Class::*)(Args...) const &&>>{};
51 }
52
53 template <typename Ret, typename Class, typename... Args>
ref_qualifiers(identity<Ret (Class::*)(Args...)volatile>)54 auto ref_qualifiers(identity<Ret (Class::*)(Args...) volatile>)
55 {
56 return std::tuple<identity<Ret (Class::*)(Args...) volatile&>,
57 identity<Ret (Class::*)(Args...) volatile &&>>{};
58 }
59
60 template <typename Ret, typename Class, typename... Args>
ref_qualifiers(identity<Ret (Class::*)(Args...)const volatile>)61 auto ref_qualifiers(identity<Ret (Class::*)(Args...) const volatile>)
62 {
63 return std::tuple<identity<Ret (Class::*)(Args...) const volatile&>,
64 identity<Ret (Class::*)(Args...) const volatile&&>>{};
65 }
66
67 template <typename T>
ref_qualifiers(identity<T>)68 auto ref_qualifiers(identity<T>)
69 {
70 return std::tuple<identity<T&>, identity<T&&>>{};
71 }
72
73 template <typename T>
all_qualifiers(identity<T> f)74 auto all_qualifiers(identity<T> f)
75 {
76 auto a = cv_qualifiers(f);
77 // make_index_sequence would work, but no need
78 auto b = ref_qualifiers(std::get<0>(a));
79 auto c = ref_qualifiers(std::get<1>(a));
80 auto d = ref_qualifiers(std::get<2>(a));
81 auto e = ref_qualifiers(std::get<3>(a));
82
83 return std::make_tuple(std::get<0>(b),
84 std::get<1>(b),
85 std::get<0>(c),
86 std::get<1>(c),
87 std::get<0>(d),
88 std::get<1>(d),
89 std::get<0>(e),
90 std::get<1>(e));
91 }
92
93 template <typename... Args>
94 struct all_invocable;
95
96 template <typename... TupleArgs, typename Class, typename... FuncArgs>
97 struct all_invocable<std::tuple<TupleArgs...>, Class, FuncArgs...>
98 {
99 using Tuple = std::tuple<TupleArgs...>;
100
101 // two ::type, becaus of identity wrapper
102 template <std::size_t N, typename T>
103 using Elem = typename std::tuple_element<N, T>::type::type;
104
105 static_assert(sizeof...(TupleArgs) == 8,
106 "all_invocable applies to each cv-ref qualified overloads");
107
108 // Class& because `&` functions can only be invokej on lvalue references
109 static constexpr bool value = internal::conjunction<
110 internal::is_invocable<Elem<0, Tuple>, Class&, FuncArgs...>,
111 internal::is_invocable<Elem<1, Tuple>, Class, FuncArgs...>,
112 internal::is_invocable<Elem<2, Tuple>, Class&, FuncArgs...>,
113 internal::is_invocable<Elem<3, Tuple>, Class, FuncArgs...>,
114 internal::is_invocable<Elem<4, Tuple>, Class&, FuncArgs...>,
115 internal::is_invocable<Elem<5, Tuple>, Class, FuncArgs...>,
116 internal::is_invocable<Elem<6, Tuple>, Class&, FuncArgs...>,
117 internal::is_invocable<Elem<7, Tuple>, Class, FuncArgs...>>::value;
118 };
119 }
120
121 namespace
122 {
regular_function_sum(int a,int b)123 int regular_function_sum(int a, int b)
124 {
125 return a + b;
126 }
127
128 template <unsigned int N, typename ...Args>
return_n_arg_type(Args &&...args)129 auto return_n_arg_type(Args&&... args) -> typename std::tuple_element<N, std::tuple<Args...>>::type
130 {
131 return std::get<N>(std::forward_as_tuple(std::forward<Args>(args)...));
132 }
133
134 struct function_object_t
135 {
136 int i = 0;
137
operator ()__anon5810dc210211::function_object_t138 int operator()(int a, int b) const
139 {
140 return a + b;
141 }
142
mutate_data__anon5810dc210211::function_object_t143 void mutate_data()
144 {
145 i = 0;
146 }
147 };
148
149 struct derived_function_object_t : function_object_t
150 {
151 };
152 }
153
154 TEST_CASE("regular function")
155 {
156 using regular_function_t = decltype(regular_function_sum);
157 using regular_function_ptr_t = std::add_pointer<decltype(regular_function_sum)>::type;
158
159 // implicit conversions work
160 static_assert(internal::is_invocable<regular_function_t, int, unsigned int>::value, "");
161 static_assert(internal::is_invocable_r<bool, regular_function_t, int, unsigned int>::value, "");
162
163 static_assert(!internal::is_invocable<regular_function_t, int, char*>::value, "");
164 static_assert(!internal::is_invocable<regular_function_t, int, char, char>::value, "");
165 static_assert(!internal::is_invocable_r<std::string, regular_function_t, int, unsigned int>::value, "");
166
167 static_assert(internal::is_invocable<regular_function_ptr_t, int, unsigned int>::value, "");
168 static_assert(internal::is_invocable_r<bool, regular_function_ptr_t, int, unsigned int>::value, "");
169
170 static_assert(!internal::is_invocable<regular_function_ptr_t, int, char*>::value, "");
171 static_assert(!internal::is_invocable<regular_function_ptr_t, int, char, char>::value, "");
172 static_assert(!internal::is_invocable_r<std::string, regular_function_ptr_t, int, unsigned int>::value, "");
173
174 REQUIRE_EQ(internal::invoke(regular_function_sum, 32, 10), 42);
175 }
176
177 TEST_CASE("regular variadic function")
178 {
179 int i = 42;
180
181 using variadic_function_t = decltype(return_n_arg_type<0, int&, float>);
182
183 static_assert(internal::is_invocable<variadic_function_t, int&, float>::value, "");
184 static_assert(internal::is_invocable_r<const int&, variadic_function_t, int&, float>::value, "");
185
186 static_assert(!internal::is_invocable<variadic_function_t, int, float>::value, "");
187 static_assert(!internal::is_invocable_r<short&, variadic_function_t, int&, float>::value, "");
188
189 REQUIRE_EQ(std::addressof(internal::invoke(return_n_arg_type<0, int&, float>, i, 2.0f)),
190 std::addressof(i));
191 }
192
193 TEST_CASE("function object")
194 {
195 static_assert(internal::is_invocable<function_object_t, int const&, double>::value, "");
196 static_assert(internal::is_invocable_r<int&&, function_object_t, int&, float>::value, "");
197
198 static_assert(!internal::is_invocable<function_object_t, int, std::string>::value, "");
199 static_assert(!internal::is_invocable_r<int&, function_object_t, int, int>::value, "");
200
201 REQUIRE_EQ(internal::invoke(function_object_t{}, 40, 2), 42);
202 }
203
204 TEST_CASE("lambda")
205 {
__anon5810dc210302(int a, int b) 206 auto add = [](int a, int b) { return a + b; };
207
208 using lambda_t = decltype(add);
209
210 static_assert(internal::is_invocable<lambda_t, int const&, double>::value, "");
211 static_assert(internal::is_invocable_r<int&&, lambda_t, int&, float>::value, "");
212
213 static_assert(!internal::is_invocable<lambda_t, int, std::string>::value, "");
214 static_assert(!internal::is_invocable_r<int&, lambda_t, int, int>::value, "");
215
216 REQUIRE_EQ(internal::invoke(add, 40, 2), 42);
217 }
218
219 TEST_CASE("member function - object reference")
220 {
221 using call_operator_t = decltype(&function_object_t::operator());
222 using mutate_data_t = decltype(&function_object_t::mutate_data);
223
224 auto qualifiers =
225 all_qualifiers(identity<int (function_object_t::*)(int, int)>{});
226 static_assert(all_invocable<decltype(qualifiers), function_object_t, int const&, double>::value, "");
227
228 static_assert(internal::is_invocable<call_operator_t, function_object_t, int const&, double>::value, "");
229 static_assert(internal::is_invocable<mutate_data_t, function_object_t>::value, "");
230 static_assert(internal::is_invocable_r<int&&, call_operator_t, function_object_t, int&, float>::value, "");
231
232 // non-const member function
233 static_assert(internal::is_invocable<mutate_data_t, function_object_t&>::value, "");
234 static_assert(!internal::is_invocable<mutate_data_t, const function_object_t&>::value, "");
235
236 static_assert(!internal::is_invocable_r<int&, call_operator_t, function_object_t, int, int>::value, "");
237
238 auto adder = function_object_t{};
239 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42);
240 }
241
242 TEST_CASE("member function - reference_wrapper<object>")
243 {
244 using call_operator_t = decltype(&function_object_t::operator());
245 using mutate_data_t = decltype(&function_object_t::mutate_data);
246 using ref_wrapper_t = std::reference_wrapper<function_object_t>;
247 using ref_wrapper_const_t = std::reference_wrapper<const function_object_t>;
248
249 static_assert(internal::is_invocable<call_operator_t, ref_wrapper_t, int const&, double>::value, "");
250 static_assert(internal::is_invocable<call_operator_t, ref_wrapper_const_t, int const&, double>::value, "");
251 static_assert(internal::is_invocable_r<int&&, call_operator_t, ref_wrapper_t, int&, float>::value, "");
252 static_assert(internal::is_invocable_r<int&&, call_operator_t, ref_wrapper_const_t, int&, float>::value, "");
253
254 // non-const member function
255 static_assert(internal::is_invocable<mutate_data_t, ref_wrapper_t>::value, "");
256 static_assert(!internal::is_invocable<mutate_data_t, ref_wrapper_const_t>::value, "");
257
258 static_assert(!internal::is_invocable_r<int&, call_operator_t, ref_wrapper_t, int, int>::value, "");
259
260 auto adder = function_object_t{};
261 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), std::ref(adder), 40, 2), 42);
262 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), std::cref(adder), 40, 2), 42);
263 }
264
265 TEST_CASE("member function - object pointer")
266 {
267 using call_operator_t = decltype(&function_object_t::operator());
268 using mutate_data_t = decltype(&function_object_t::mutate_data);
269
270 static_assert(internal::is_invocable<call_operator_t, function_object_t*, int const&, double>::value, "");
271 static_assert(internal::is_invocable_r<int&&, call_operator_t, function_object_t*, int&, float>::value, "");
272
273 // non-const member function
274 static_assert(internal::is_invocable<mutate_data_t, function_object_t*>::value, "");
275 static_assert(!internal::is_invocable<mutate_data_t, const function_object_t*>::value, "");
276
277 static_assert(!internal::is_invocable_r<int&, call_operator_t, function_object_t*, int, int>::value, "");
278
279 auto adder = function_object_t{};
280 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), &adder, 40, 2), 42);
281 }
282
283 TEST_CASE("member function - derived object reference")
284 {
285 using call_operator_t = decltype(&function_object_t::operator());
286 using mutate_data_t = decltype(&function_object_t::mutate_data);
287
288 // should split all_qualifiers to get specific ones, right now it cannot
289 // be used to test const objects and reference_wrapper.
290 // Need to add make_index_sequence to do that properly.
291 auto qualifiers =
292 all_qualifiers(identity<int (function_object_t::*)(int, int)>{});
293 #if !defined(FPLUS_MSVC2015_BYPASS_FAILING_TESTS) // Error C2338 under MSVC (i.e static_assert fail)
294 static_assert(all_invocable<decltype(qualifiers), derived_function_object_t, int const&, double>::value, "");
295 #endif
296
297 static_assert(internal::is_invocable<call_operator_t, derived_function_object_t, int const&, double>::value, "");
298 static_assert(internal::is_invocable_r<int&&, call_operator_t, derived_function_object_t, int&, float>::value, "");
299
300 // non-const member function
301 static_assert(internal::is_invocable<mutate_data_t, derived_function_object_t&>::value, "");
302 static_assert(!internal::is_invocable<mutate_data_t, const derived_function_object_t&>::value, "");
303
304 static_assert(!internal::is_invocable_r<int&, call_operator_t, derived_function_object_t&, int, int>::value, "");
305
306 auto adder = derived_function_object_t{};
307 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42);
308 }
309
310 TEST_CASE("member function - reference_wrapper<derived object>")
311 {
312 using call_operator_t = decltype(&function_object_t::operator());
313 using mutate_data_t = decltype(&function_object_t::mutate_data);
314
315 using ref_wrapper_t = std::reference_wrapper<derived_function_object_t>;
316 using ref_wrapper_const_t = std::reference_wrapper<const derived_function_object_t>;
317
318 static_assert(internal::is_invocable<call_operator_t, ref_wrapper_t, int const&, double>::value, "");
319 static_assert(internal::is_invocable<call_operator_t, ref_wrapper_const_t, int const&, double>::value, "");
320 static_assert(internal::is_invocable_r<int&&, call_operator_t, ref_wrapper_t, int&, float>::value, "");
321
322 // non-const member function
323 static_assert(internal::is_invocable<mutate_data_t, ref_wrapper_t>::value, "");
324 static_assert(!internal::is_invocable<mutate_data_t, ref_wrapper_const_t>::value, "");
325
326 static_assert(!internal::is_invocable_r<int&, call_operator_t, ref_wrapper_t&, int, int>::value, "");
327
328 auto adder = derived_function_object_t{};
329 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), adder, 40, 2), 42);
330 }
331
332 TEST_CASE("member function - derived object pointer")
333 {
334 using call_operator_t = decltype(&function_object_t::operator());
335 using mutate_data_t = decltype(&function_object_t::mutate_data);
336
337 static_assert(internal::is_invocable<call_operator_t, derived_function_object_t*, int const&, double>::value, "");
338 static_assert(internal::is_invocable_r<int&&, call_operator_t, derived_function_object_t*, int&, float>::value, "");
339
340 // non-const non-volatile member function
341 static_assert(internal::is_invocable<mutate_data_t, derived_function_object_t*>::value, "");
342 static_assert(!internal::is_invocable<mutate_data_t, const derived_function_object_t*>::value, "");
343 static_assert(!internal::is_invocable<mutate_data_t, volatile derived_function_object_t*>::value, "");
344
345 static_assert(!internal::is_invocable_r<int&, call_operator_t, derived_function_object_t*, int, int>::value, "");
346
347 auto adder = derived_function_object_t{};
348 REQUIRE_EQ(internal::invoke(&function_object_t::operator(), &adder, 40, 2), 42);
349 }
350
351 TEST_CASE("member data - object reference")
352 {
353 using member_data_t = decltype(&function_object_t::i);
354
355 static_assert(internal::is_invocable<member_data_t, function_object_t>::value, "");
356 static_assert(internal::is_invocable_r<int&&, member_data_t, function_object_t>::value, "");
357
358 // cannot convert lvalue ref to rvalue-reference
359 static_assert(!internal::is_invocable_r<int&&, member_data_t, function_object_t&>::value, "");
360
361 static_assert(!internal::is_invocable<member_data_t, function_object_t, int>::value, "");
362
363 auto obj = function_object_t{};
364 obj.i = 42;
365 REQUIRE_EQ(internal::invoke(&function_object_t::i, obj), 42);
366 }
367
368 TEST_CASE("member data - reference_wrapper<object>")
369 {
370 using member_data_t = decltype(&function_object_t::i);
371 using ref_wrapper_t = std::reference_wrapper<function_object_t>;
372 using ref_wrapper_const_t = std::reference_wrapper<const function_object_t>;
373
374 static_assert(internal::is_invocable<member_data_t, ref_wrapper_t>::value, "");
375 static_assert(internal::is_invocable_r<int const&, member_data_t, ref_wrapper_const_t>::value, "");
376
377 // cannot convert lvalue ref to rvalue-reference
378 static_assert(!internal::is_invocable_r<int&&, member_data_t, ref_wrapper_t>::value, "");
379 // nor from const lvalue reference to non-const lvalue reference
380 static_assert(!internal::is_invocable_r<int&, member_data_t, ref_wrapper_const_t>::value, "");
381
382 auto obj = function_object_t{};
383 obj.i = 42;
384
385 REQUIRE_EQ(internal::invoke(&function_object_t::i, std::ref(obj)), 42);
386 REQUIRE_EQ(internal::invoke(&function_object_t::i, std::cref(obj)), 42);
387 }
388
389 TEST_CASE("member data - object pointer")
390 {
391 using member_data_t = decltype(&function_object_t::i);
392
393 static_assert(internal::is_invocable<member_data_t, function_object_t*>::value, "");
394 static_assert(internal::is_invocable_r<int&, member_data_t, function_object_t*>::value, "");
395
396 // cannot convert lvalue ref to rvalue-reference
397 static_assert(!internal::is_invocable_r<int&&, member_data_t, function_object_t*>::value, "");
398 static_assert(!internal::is_invocable_r<int&, member_data_t, const function_object_t*>::value, "");
399
400 static_assert(!internal::is_invocable<member_data_t, function_object_t*, int>::value, "");
401
402 auto obj = function_object_t{};
403 obj.i = 42;
404 REQUIRE_EQ(internal::invoke(&function_object_t::i, &obj), 42);
405 }
406
407 TEST_CASE("member data - derived object reference")
408 {
409 using member_data_t = decltype(&function_object_t::i);
410
411 static_assert(internal::is_invocable<member_data_t, derived_function_object_t>::value, "");
412 static_assert(internal::is_invocable_r<int&&, member_data_t, derived_function_object_t>::value, "");
413
414 // cannot convert lvalue ref to rvalue-reference
415 static_assert(!internal::is_invocable_r<int&&, member_data_t, derived_function_object_t&>::value, "");
416
417 static_assert(!internal::is_invocable<member_data_t, derived_function_object_t, int>::value, "");
418
419 auto obj = derived_function_object_t{};
420 obj.i = 42;
421 REQUIRE_EQ(internal::invoke(&function_object_t::i, obj), 42);
422 }
423
424 TEST_CASE("member data - reference_wrapper<derived object>")
425 {
426 using member_data_t = decltype(&function_object_t::i);
427
428 using ref_wrapper_t = std::reference_wrapper<derived_function_object_t>;
429 using ref_wrapper_const_t = std::reference_wrapper<const derived_function_object_t>;
430
431 static_assert(internal::is_invocable<member_data_t, ref_wrapper_t>::value, "");
432 static_assert(internal::is_invocable_r<int const&, member_data_t, ref_wrapper_const_t>::value, "");
433
434 // cannot convert lvalue ref to rvalue-reference
435 static_assert(!internal::is_invocable_r<int&&, member_data_t, ref_wrapper_t>::value, "");
436 // nor from const lvalue reference to non-const lvalue reference
437 static_assert(!internal::is_invocable_r<int&, member_data_t, ref_wrapper_const_t>::value, "");
438
439 auto obj = derived_function_object_t{};
440 obj.i = 42;
441
442 REQUIRE_EQ(internal::invoke(&function_object_t::i, std::ref(obj)), 42);
443 REQUIRE_EQ(internal::invoke(&function_object_t::i, std::cref(obj)), 42);
444 }
445
446 TEST_CASE("member data - derived object pointer")
447 {
448 using member_data_t = decltype(&function_object_t::i);
449
450 static_assert(internal::is_invocable<member_data_t, derived_function_object_t*>::value, "");
451 static_assert(internal::is_invocable_r<int&, member_data_t, derived_function_object_t*>::value, "");
452
453 // cannot convert lvalue ref to rvalue-reference
454 static_assert(!internal::is_invocable_r<int&&, member_data_t, derived_function_object_t*>::value, "");
455 static_assert(!internal::is_invocable_r<int&, member_data_t, const derived_function_object_t*>::value, "");
456
457 static_assert(!internal::is_invocable<member_data_t, derived_function_object_t*, int>::value, "");
458
459 auto obj = derived_function_object_t{};
460 obj.i = 42;
461 REQUIRE_EQ(internal::invoke(&derived_function_object_t::i, &obj), 42);
462 }
463
464 #ifdef __GNUC__
465 #pragma GCC diagnostic push
466 #pragma GCC diagnostic ignored "-Wconversion"
467 #endif // __GNUC__
468 TEST_CASE("generic lambda")
469 {
__anon5810dc210402(auto a, auto b) 470 auto add = [](auto a, auto b) { return a + b; };
471
472 using lambda_t = decltype(add);
473
474 static_assert(internal::is_invocable<lambda_t, int const&, double>::value, "");
475 static_assert(internal::is_invocable_r<int&&, lambda_t, int&, float>::value, "");
476
477 // compile error, static_assert doesn't trigger though
478 // from cppreference:
479 //
480 // Formally, determines whether INVOKE(declval<Fn>(),
481 // declval<ArgTypes>()...) is well formed when treated as an unevaluated
482 // operand, where INVOKE is the operation defined in Callable.
483 //
484 // This is indeed well-formed in the unevaluated context...
485 // static_assert(!internal::is_invocable<lambda_t, int, std::string>::value, "");
486
487 static_assert(!internal::is_invocable_r<int&, lambda_t, int, int>::value, "");
488
489 REQUIRE_EQ(internal::invoke(add, 40, 2), 42);
490 }
491
492 TEST_CASE("transparent function objects")
493 {
494 static_assert(internal::is_invocable<std::plus<>, int, int>::value, "");
495 static_assert(internal::is_invocable<std::plus<>, int, float>::value, "");
496
497 REQUIRE_EQ(internal::invoke(std::plus<>{}, 40, 2), 42);
498 }
499
500 #ifdef __GNUC__
501 #pragma GCC diagnostic pop
502 #endif // __GNUC__
503