1// Copyright 2017 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// absl::base_internal::invoke(f, args...) is an implementation of
16// INVOKE(f, args...) from section [func.require] of the C++ standard.
17// When compiled as C++17 and later versions, it is implemented as an alias of
18// std::invoke.
19//
20// [func.require]
21// Define INVOKE (f, t1, t2, ..., tN) as follows:
22// 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
23// and t1 is an object of type T or a reference to an object of type T or a
24// reference to an object of a type derived from T;
25// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
26// class T and t1 is not one of the types described in the previous item;
27// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
28// an object of type T or a reference to an object of type T or a reference
29// to an object of a type derived from T;
30// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
31// is not one of the types described in the previous item;
32// 5. f(t1, t2, ..., tN) in all other cases.
33//
34// The implementation is SFINAE-friendly: substitution failure within invoke()
35// isn't an error.
36
37#ifndef ABSL_BASE_INTERNAL_INVOKE_H_
38#define ABSL_BASE_INTERNAL_INVOKE_H_
39
40#include "absl/base/config.h"
41
42#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
43
44#include <functional>
45
46namespace absl {
47ABSL_NAMESPACE_BEGIN
48namespace base_internal {
49
50using std::invoke;
51using std::invoke_result_t;
52using std::is_invocable_r;
53
54} // namespace base_internal
55ABSL_NAMESPACE_END
56} // namespace absl
57
58#else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
59
60#include <algorithm>
61#include <type_traits>
62#include <utility>
63
64#include "absl/meta/type_traits.h"
65
66// The following code is internal implementation detail. See the comment at the
67// top of this file for the API documentation.
68
69namespace absl {
70ABSL_NAMESPACE_BEGIN
71namespace base_internal {
72
73// The five classes below each implement one of the clauses from the definition
74// of INVOKE. The inner class template Accept<F, Args...> checks whether the
75// clause is applicable; static function template Invoke(f, args...) does the
76// invocation.
77//
78// By separating the clause selection logic from invocation we make sure that
79// Invoke() does exactly what the standard says.
80
81template <typename Derived>
82struct StrippedAccept {
83 template <typename... Args>
84 struct Accept : Derived::template AcceptImpl<typename std::remove_cv<
85 typename std::remove_reference<Args>::type>::type...> {};
86};
87
88// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T
89// and t1 is an object of type T or a reference to an object of type T or a
90// reference to an object of a type derived from T.
91struct MemFunAndRef : StrippedAccept<MemFunAndRef> {
92 template <typename... Args>
93 struct AcceptImpl : std::false_type {};
94
95 template <typename MemFunType, typename C, typename Obj, typename... Args>
96 struct AcceptImpl<MemFunType C::*, Obj, Args...>
97 : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
98 absl::is_function<MemFunType>::value> {
99 };
100
101 template <typename MemFun, typename Obj, typename... Args>
102 static decltype((std::declval<Obj>().*
103 std::declval<MemFun>())(std::declval<Args>()...))
104 Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) {
105// Ignore bogus GCC warnings on this line.
106// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example.
107#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0)
108#pragma GCC diagnostic push
109#pragma GCC diagnostic ignored "-Warray-bounds"
110#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
111#endif
112 return (std::forward<Obj>(obj).*
113 std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
114#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0)
115#pragma GCC diagnostic pop
116#endif
117 }
118};
119
120// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
121// class T and t1 is not one of the types described in the previous item.
122struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> {
123 template <typename... Args>
124 struct AcceptImpl : std::false_type {};
125
126 template <typename MemFunType, typename C, typename Ptr, typename... Args>
127 struct AcceptImpl<MemFunType C::*, Ptr, Args...>
128 : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
129 absl::is_function<MemFunType>::value> {
130 };
131
132 template <typename MemFun, typename Ptr, typename... Args>
133 static decltype(((*std::declval<Ptr>()).*
134 std::declval<MemFun>())(std::declval<Args>()...))
135 Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) {
136 return ((*std::forward<Ptr>(ptr)).*
137 std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...);
138 }
139};
140
141// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is
142// an object of type T or a reference to an object of type T or a reference
143// to an object of a type derived from T.
144struct DataMemAndRef : StrippedAccept<DataMemAndRef> {
145 template <typename... Args>
146 struct AcceptImpl : std::false_type {};
147
148 template <typename R, typename C, typename Obj>
149 struct AcceptImpl<R C::*, Obj>
150 : std::integral_constant<bool, std::is_base_of<C, Obj>::value &&
151 !absl::is_function<R>::value> {};
152
153 template <typename DataMem, typename Ref>
154 static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke(
155 DataMem&& data_mem, Ref&& ref) {
156 return std::forward<Ref>(ref).*std::forward<DataMem>(data_mem);
157 }
158};
159
160// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1
161// is not one of the types described in the previous item.
162struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> {
163 template <typename... Args>
164 struct AcceptImpl : std::false_type {};
165
166 template <typename R, typename C, typename Ptr>
167 struct AcceptImpl<R C::*, Ptr>
168 : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value &&
169 !absl::is_function<R>::value> {};
170
171 template <typename DataMem, typename Ptr>
172 static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke(
173 DataMem&& data_mem, Ptr&& ptr) {
174 return (*std::forward<Ptr>(ptr)).*std::forward<DataMem>(data_mem);
175 }
176};
177
178// f(t1, t2, ..., tN) in all other cases.
179struct Callable {
180 // Callable doesn't have Accept because it's the last clause that gets picked
181 // when none of the previous clauses are applicable.
182 template <typename F, typename... Args>
183 static decltype(std::declval<F>()(std::declval<Args>()...)) Invoke(
184 F&& f, Args&&... args) {
185 return std::forward<F>(f)(std::forward<Args>(args)...);
186 }
187};
188
189// Resolves to the first matching clause.
190template <typename... Args>
191struct Invoker {
192 typedef typename std::conditional<
193 MemFunAndRef::Accept<Args...>::value, MemFunAndRef,
194 typename std::conditional<
195 MemFunAndPtr::Accept<Args...>::value, MemFunAndPtr,
196 typename std::conditional<
197 DataMemAndRef::Accept<Args...>::value, DataMemAndRef,
198 typename std::conditional<DataMemAndPtr::Accept<Args...>::value,
199 DataMemAndPtr, Callable>::type>::type>::
200 type>::type type;
201};
202
203// The result type of Invoke<F, Args...>.
204template <typename F, typename... Args>
205using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke(
206 std::declval<F>(), std::declval<Args>()...));
207
208// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section
209// [func.require] of the C++ standard.
210template <typename F, typename... Args>
211invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) {
212 return Invoker<F, Args...>::type::Invoke(std::forward<F>(f),
213 std::forward<Args>(args)...);
214}
215
216template <typename AlwaysVoid, typename, typename, typename...>
217struct IsInvocableRImpl : std::false_type {};
218
219template <typename R, typename F, typename... Args>
220struct IsInvocableRImpl<
221 absl::void_t<absl::base_internal::invoke_result_t<F, Args...> >, R, F,
222 Args...>
223 : std::integral_constant<
224 bool,
225 std::is_convertible<absl::base_internal::invoke_result_t<F, Args...>,
226 R>::value ||
227 std::is_void<R>::value> {};
228
229// Type trait whose member `value` is true if invoking `F` with `Args` is valid,
230// and either the return type is convertible to `R`, or `R` is void.
231// C++11-compatible version of `std::is_invocable_r`.
232template <typename R, typename F, typename... Args>
233using is_invocable_r = IsInvocableRImpl<void, R, F, Args...>;
234
235} // namespace base_internal
236ABSL_NAMESPACE_END
237} // namespace absl
238
239#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
240
241#endif // ABSL_BASE_INTERNAL_INVOKE_H_
242