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 #pragma once
8 
9 #include <functional>
10 #include <type_traits>
11 #include <utility>
12 
13 #include <fplus/internal/meta.hpp>
14 
15 // borrowed to libc++
16 #define FPLUS_INVOKE_RETURN(...) \
17   ->decltype(__VA_ARGS__)        \
18   {                              \
19     return __VA_ARGS__;          \
20   }
21 
22 namespace fplus
23 {
24 namespace internal
25 {
26 // We need std::invoke to detect callable objects
27 //
28 // source:
29 // http://en.cppreference.com/mwiki/index.php?title=cpp/utility/functional/invoke&oldid=82514
30 template <typename U>
31 static std::true_type is_refwrap_test(const std::reference_wrapper<U>&);
32 
33 template <typename U>
34 static std::false_type is_refwrap_test(const U&);
35 
36 template <typename T>
37 struct is_reference_wrapper : decltype(is_refwrap_test(std::declval<T>()))
38 {
39 };
40 
41 template <typename T, typename U = typename std::decay<T>::type>
42 struct unwrap_reference_wrapper
43 {
44   using type = T;
45 };
46 
47 template <typename T, typename U>
48 struct unwrap_reference_wrapper<T, std::reference_wrapper<U>>
49 {
50   using type = U&;
51 };
52 
53 template <typename T>
54 using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper<T>::type;
55 
56 // note: clang only triggers the second static_assert
57 //      - static_assert(is_invocable<&base_class::non_const_method, const derived_class&>::value, "");
58 //      - static_assert(is_invocable<&base_class::non_const_method, const base_class&>::value, "");
59 // GCC triggers both. To workaround this clang bug, we have to manage cv correctness ourselves
60 
61 template <typename T>
62 struct is_const_member_function : std::false_type
63 {
64 };
65 
66 // decay doesn't add pointer to abominable functions, don't bother writing them
67 template <typename R, typename... Args>
68 struct is_const_member_function<R(Args...) const> : std::true_type
69 {
70 };
71 
72 template <typename R, typename... Args>
73 struct is_const_member_function<R(Args...) const&> : std::true_type
74 {
75 };
76 
77 template <typename R, typename... Args>
78 struct is_const_member_function<R(Args...) const&&> : std::true_type
79 {
80 };
81 
82 template <typename R, typename... Args>
83 struct is_const_member_function<R(Args...) const volatile> : std::true_type
84 {
85 };
86 
87 template <typename R, typename... Args>
88 struct is_const_member_function<R(Args...) const volatile&> : std::true_type
89 {
90 };
91 
92 template <typename R, typename... Args>
93 struct is_const_member_function<R(Args...) const volatile&&> : std::true_type
94 {
95 };
96 
97 template <typename T>
98 struct is_volatile_member_function : std::false_type
99 {
100 };
101 
102 // decay doesn't add pointer to abominable functions, don't bother writing them
103 template <typename R, typename... Args>
104 struct is_volatile_member_function<R(Args...) volatile> : std::true_type
105 {
106 };
107 
108 template <typename R, typename... Args>
109 struct is_volatile_member_function<R(Args...) volatile&> : std::true_type
110 {
111 };
112 
113 template <typename R, typename... Args>
114 struct is_volatile_member_function<R(Args...) volatile&&> : std::true_type
115 {
116 };
117 
118 template <typename R, typename... Args>
119 struct is_volatile_member_function<R(Args...) const volatile> : std::true_type
120 {
121 };
122 
123 template <typename R, typename... Args>
124 struct is_volatile_member_function<R(Args...) const volatile&> : std::true_type
125 {
126 };
127 
128 template <typename R, typename... Args>
129 struct is_volatile_member_function<R(Args...) const volatile&&> : std::true_type
130 {
131 };
132 
133 template <typename Object, typename Signature>
134 struct has_correct_cv
135 {
136   // if object has no cv, every method can be called
137   // else the method must have the same cv than the object
138   static constexpr bool value =
139       std::is_same<typename std::remove_cv<Object>::type, Object>::value ||
140       ((is_volatile_member_function<Signature>::value ==
141         std::is_volatile<Object>::value) &&
142        (is_const_member_function<Signature>::value ==
143         std::is_const<Object>::value));
144 };
145 
146 // pointer to member function - reference to object
147 template <
148     typename Base,
149     typename T,
150     typename Derived,
151     typename... Args,
152     typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
153     typename std::enable_if<
154         is_function<T>::value &&
155             has_correct_cv<typename std::remove_reference<Unwrapped>::type, T>::value &&
156             std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
157         int>::type = 0>
158 inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args)
159     FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*
160                          pmf)(std::forward<Args>(args)...))
161 
162 // pointer to member function - pointer to object
163 template <
164     typename Base,
165     typename T,
166     typename Pointer,
167     typename... Args,
168     typename std::enable_if<
169         is_function<T>::value &&
170             has_correct_cv<typename std::remove_pointer<
171                                typename std::decay<Pointer>::type>::type,
172                            T>::value &&
173             !std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
174         int>::type = 0>
175 inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args)
176     FPLUS_INVOKE_RETURN(((*std::forward<Pointer>(ptr)).*
177                          pmf)(std::forward<Args>(args)...))
178 
179 // pointer to non-static data member - reference to object
180 template <
181     typename Base,
182     typename T,
183     typename Derived,
184     typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
185     typename std::enable_if<
186         !is_function<T>::value &&
187             std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
188         int>::type = 0>
189 inline auto invoke_impl(T Base::*pmd, Derived&& ref)
190     FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*pmd))
191 
192 // pointer to non-static data member - pointer to object
193 template <
194     typename Base,
195     typename T,
196     typename Pointer,
197     typename std::enable_if<
198         !is_function<T>::value &&
199             !std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
200         int>::type = 0>
201 inline auto invoke_impl(T Base::*pmd, Pointer&& ptr)
202     FPLUS_INVOKE_RETURN((*std::forward<Pointer>(ptr)).*pmd)
203 
204 // normal case - functions, lambdas, function objects
205 template <typename F,
206           typename... Args,
207           typename std::enable_if<
208               !std::is_member_pointer<typename std::decay<F>::type>::value,
209               int>::type = 0>
210 inline auto invoke_impl(F&& f, Args&&... args)
211     FPLUS_INVOKE_RETURN((std::forward<F>(f)(std::forward<Args>(args)...)))
212 
213 template <typename AlwaysVoid, typename, typename...>
214 struct invoke_result_impl
215 {
216 };
217 
218 template <typename F, typename... Args>
219 struct invoke_result_impl<decltype(void(invoke_impl(std::declval<F>(),
220                                                     std::declval<Args>()...))),
221                           F,
222                           Args...>
223 {
224   using type =
225       decltype(invoke_impl(std::declval<F>(), std::declval<Args>()...));
226 };
227 
228 template <typename F, typename... ArgTypes>
229 struct invoke_result : invoke_result_impl<void, F, ArgTypes...>
230 {
231 };
232 
233 template <typename F, typename... Args>
234 using invoke_result_t = typename invoke_result<F, Args...>::type;
235 
236 // noexcept omitted on purpose, cannot be implemented without C++17.
237 // GCC 7.1 works with libstdc++, but clang fails, even with latest build,
238 // on both libstdc++/libc++, I suspect an internal compiler trait is at
239 // play to make GCC work.
240 //
241 // We could detect if C++17 is used and use std::invoke directly.
242 template <typename F, typename... ArgTypes>
invoke(F && f,ArgTypes &&...args)243 invoke_result_t<F, ArgTypes...> invoke(F&& f, ArgTypes&&... args)
244 {
245   return invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...);
246 }
247 
248 // Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed)
249 template <typename Result, typename ReturnType, typename = void>
250 struct is_invocable_impl : std::false_type
251 {
252 };
253 
254 template <typename Result, typename ReturnType>
255 struct is_invocable_impl<Result, ReturnType, void_t<typename Result::type>>
256     : disjunction<std::is_void<ReturnType>,
257                   std::is_convertible<typename Result::type, ReturnType>>::type
258 {
259 };
260 
261 template <typename F, typename... ArgTypes>
262 struct is_invocable
263     : is_invocable_impl<invoke_result<F, ArgTypes...>, void>::type
264 {
265 };
266 
267 template <typename ReturnType, typename F, typename... ArgTypes>
268 struct is_invocable_r
269     : is_invocable_impl<invoke_result<F, ArgTypes...>, ReturnType>::type
270 {
271 };
272 }
273 }
274 
275 #undef FPLUS_INVOKE_RETURN
276