/src/abseil-cpp/absl/functional/internal/any_invocable.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2022 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 | | // Implementation details for `absl::AnyInvocable` |
16 | | |
17 | | #ifndef ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ |
18 | | #define ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ |
19 | | |
20 | | //////////////////////////////////////////////////////////////////////////////// |
21 | | // // |
22 | | // This implementation chooses between local storage and remote storage for // |
23 | | // the contained target object based on the target object's size, alignment // |
24 | | // requirements, and whether or not it has a nothrow move constructor. // |
25 | | // Additional optimizations are performed when the object is a trivially // |
26 | | // copyable type [basic.types]. // |
27 | | // // |
28 | | // There are three datamembers per `AnyInvocable` instance // |
29 | | // // |
30 | | // 1) A union containing either // |
31 | | // - A pointer to the target object referred to via a void*, or // |
32 | | // - the target object, emplaced into a raw char buffer // |
33 | | // // |
34 | | // 2) A function pointer to a "manager" function operation that takes a // |
35 | | // discriminator and logically branches to either perform a move operation // |
36 | | // or destroy operation based on that discriminator. // |
37 | | // // |
38 | | // 3) A function pointer to an "invoker" function operation that invokes the // |
39 | | // target object, directly returning the result. // |
40 | | // // |
41 | | // When in the logically empty state, the manager function is an empty // |
42 | | // function and the invoker function is one that would be undefined behavior // |
43 | | // to call. // |
44 | | // // |
45 | | // An additional optimization is performed when converting from one // |
46 | | // AnyInvocable to another where only the noexcept specification and/or the // |
47 | | // cv/ref qualifiers of the function type differ. In these cases, the // |
48 | | // conversion works by "moving the guts", similar to if they were the same // |
49 | | // exact type, as opposed to having to perform an additional layer of // |
50 | | // wrapping through remote storage. // |
51 | | // // |
52 | | //////////////////////////////////////////////////////////////////////////////// |
53 | | |
54 | | // IWYU pragma: private, include "absl/functional/any_invocable.h" |
55 | | |
56 | | #include <cassert> |
57 | | #include <cstddef> |
58 | | #include <cstring> |
59 | | #include <exception> |
60 | | #include <functional> |
61 | | #include <memory> |
62 | | #include <new> |
63 | | #include <type_traits> |
64 | | #include <utility> |
65 | | |
66 | | #include "absl/base/attributes.h" |
67 | | #include "absl/base/config.h" |
68 | | #include "absl/base/internal/invoke.h" |
69 | | #include "absl/base/macros.h" |
70 | | #include "absl/base/optimization.h" |
71 | | #include "absl/meta/type_traits.h" |
72 | | #include "absl/utility/utility.h" |
73 | | |
74 | | namespace absl { |
75 | | ABSL_NAMESPACE_BEGIN |
76 | | |
77 | | // Helper macro used to prevent spelling `noexcept` in language versions older |
78 | | // than C++17, where it is not part of the type system, in order to avoid |
79 | | // compilation failures and internal compiler errors. |
80 | | #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
81 | | #define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex) |
82 | | #else |
83 | | #define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) |
84 | | #endif |
85 | | |
86 | | // Defined in functional/any_invocable.h |
87 | | template <class Sig> |
88 | | class AnyInvocable; |
89 | | |
90 | | namespace internal_any_invocable { |
91 | | |
92 | | // Constants relating to the small-object-storage for AnyInvocable |
93 | | enum StorageProperty : std::size_t { |
94 | | kAlignment = alignof(std::max_align_t), // The alignment of the storage |
95 | | kStorageSize = sizeof(void*) * 2 // The size of the storage |
96 | | }; |
97 | | |
98 | | //////////////////////////////////////////////////////////////////////////////// |
99 | | // |
100 | | // A metafunction for checking if a type is an AnyInvocable instantiation. |
101 | | // This is used during conversion operations. |
102 | | template <class T> |
103 | | struct IsAnyInvocable : std::false_type {}; |
104 | | |
105 | | template <class Sig> |
106 | | struct IsAnyInvocable<AnyInvocable<Sig>> : std::true_type {}; |
107 | | // |
108 | | //////////////////////////////////////////////////////////////////////////////// |
109 | | |
110 | | // A type trait that tells us whether or not a target function type should be |
111 | | // stored locally in the small object optimization storage |
112 | | template <class T> |
113 | | using IsStoredLocally = std::integral_constant< |
114 | | bool, sizeof(T) <= kStorageSize && alignof(T) <= kAlignment && |
115 | | kAlignment % alignof(T) == 0 && |
116 | | std::is_nothrow_move_constructible<T>::value>; |
117 | | |
118 | | // An implementation of std::remove_cvref_t of C++20. |
119 | | template <class T> |
120 | | using RemoveCVRef = |
121 | | typename std::remove_cv<typename std::remove_reference<T>::type>::type; |
122 | | |
123 | | //////////////////////////////////////////////////////////////////////////////// |
124 | | // |
125 | | // An implementation of the C++ standard INVOKE<R> pseudo-macro, operation is |
126 | | // equivalent to std::invoke except that it forces an implicit conversion to the |
127 | | // specified return type. If "R" is void, the function is executed and the |
128 | | // return value is simply ignored. |
129 | | template <class ReturnType, class F, class... P, |
130 | | typename = absl::enable_if_t<std::is_void<ReturnType>::value>> |
131 | | void InvokeR(F&& f, P&&... args) { |
132 | | absl::base_internal::invoke(std::forward<F>(f), std::forward<P>(args)...); |
133 | | } |
134 | | |
135 | | template <class ReturnType, class F, class... P, |
136 | | absl::enable_if_t<!std::is_void<ReturnType>::value, int> = 0> |
137 | | ReturnType InvokeR(F&& f, P&&... args) { |
138 | | // GCC 12 has a false-positive -Wmaybe-uninitialized warning here. |
139 | | #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) |
140 | | #pragma GCC diagnostic push |
141 | | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" |
142 | | #endif |
143 | | return absl::base_internal::invoke(std::forward<F>(f), |
144 | | std::forward<P>(args)...); |
145 | | #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) |
146 | | #pragma GCC diagnostic pop |
147 | | #endif |
148 | | } |
149 | | |
150 | | // |
151 | | //////////////////////////////////////////////////////////////////////////////// |
152 | | |
153 | | //////////////////////////////////////////////////////////////////////////////// |
154 | | /// |
155 | | // A metafunction that takes a "T" corresponding to a parameter type of the |
156 | | // user's specified function type, and yields the parameter type to use for the |
157 | | // type-erased invoker. In order to prevent observable moves, this must be |
158 | | // either a reference or, if the type is trivial, the original parameter type |
159 | | // itself. Since the parameter type may be incomplete at the point that this |
160 | | // metafunction is used, we can only do this optimization for scalar types |
161 | | // rather than for any trivial type. |
162 | | template <typename T> |
163 | | T ForwardImpl(std::true_type); |
164 | | |
165 | | template <typename T> |
166 | | T&& ForwardImpl(std::false_type); |
167 | | |
168 | | // NOTE: We deliberately use an intermediate struct instead of a direct alias, |
169 | | // as a workaround for b/206991861 on MSVC versions < 1924. |
170 | | template <class T> |
171 | | struct ForwardedParameter { |
172 | | using type = decltype(( |
173 | | ForwardImpl<T>)(std::integral_constant<bool, |
174 | | std::is_scalar<T>::value>())); |
175 | | }; |
176 | | |
177 | | template <class T> |
178 | | using ForwardedParameterType = typename ForwardedParameter<T>::type; |
179 | | // |
180 | | //////////////////////////////////////////////////////////////////////////////// |
181 | | |
182 | | // A discriminator when calling the "manager" function that describes operation |
183 | | // type-erased operation should be invoked. |
184 | | // |
185 | | // "relocate_from_to" specifies that the manager should perform a move. |
186 | | // |
187 | | // "dispose" specifies that the manager should perform a destroy. |
188 | | enum class FunctionToCall : bool { relocate_from_to, dispose }; |
189 | | |
190 | | // The portion of `AnyInvocable` state that contains either a pointer to the |
191 | | // target object or the object itself in local storage |
192 | | union TypeErasedState { |
193 | | struct { |
194 | | // A pointer to the type-erased object when remotely stored |
195 | | void* target; |
196 | | // The size of the object for `RemoteManagerTrivial` |
197 | | std::size_t size; |
198 | | } remote; |
199 | | |
200 | | // Local-storage for the type-erased object when small and trivial enough |
201 | | alignas(kAlignment) char storage[kStorageSize]; |
202 | | }; |
203 | | |
204 | | // A typed accessor for the object in `TypeErasedState` storage |
205 | | template <class T> |
206 | | T& ObjectInLocalStorage(TypeErasedState* const state) { |
207 | | // We launder here because the storage may be reused with the same type. |
208 | | #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L |
209 | | return *std::launder(reinterpret_cast<T*>(&state->storage)); |
210 | | #elif ABSL_HAVE_BUILTIN(__builtin_launder) |
211 | | return *__builtin_launder(reinterpret_cast<T*>(&state->storage)); |
212 | | #else |
213 | | |
214 | | // When `std::launder` or equivalent are not available, we rely on undefined |
215 | | // behavior, which works as intended on Abseil's officially supported |
216 | | // platforms as of Q2 2022. |
217 | | #if !defined(__clang__) && defined(__GNUC__) |
218 | | #pragma GCC diagnostic push |
219 | | #pragma GCC diagnostic ignored "-Wstrict-aliasing" |
220 | | #endif |
221 | | return *reinterpret_cast<T*>(&state->storage); |
222 | | #if !defined(__clang__) && defined(__GNUC__) |
223 | | #pragma GCC diagnostic pop |
224 | | #endif |
225 | | |
226 | | #endif |
227 | | } |
228 | | |
229 | | // The type for functions issuing lifetime-related operations: move and dispose |
230 | | // A pointer to such a function is contained in each `AnyInvocable` instance. |
231 | | // NOTE: When specifying `FunctionToCall::`dispose, the same state must be |
232 | | // passed as both "from" and "to". |
233 | | using ManagerType = void(FunctionToCall /*operation*/, |
234 | | TypeErasedState* /*from*/, TypeErasedState* /*to*/) |
235 | | ABSL_INTERNAL_NOEXCEPT_SPEC(true); |
236 | | |
237 | | // The type for functions issuing the actual invocation of the object |
238 | | // A pointer to such a function is contained in each AnyInvocable instance. |
239 | | template <bool SigIsNoexcept, class ReturnType, class... P> |
240 | | using InvokerType = ReturnType(TypeErasedState*, ForwardedParameterType<P>...) |
241 | | ABSL_INTERNAL_NOEXCEPT_SPEC(SigIsNoexcept); |
242 | | |
243 | | // The manager that is used when AnyInvocable is empty |
244 | | inline void EmptyManager(FunctionToCall /*operation*/, |
245 | | TypeErasedState* /*from*/, |
246 | 0 | TypeErasedState* /*to*/) noexcept {} |
247 | | |
248 | | // The manager that is used when a target function is in local storage and is |
249 | | // a trivially copyable type. |
250 | | inline void LocalManagerTrivial(FunctionToCall /*operation*/, |
251 | | TypeErasedState* const from, |
252 | 0 | TypeErasedState* const to) noexcept { |
253 | 0 | // This single statement without branching handles both possible operations. |
254 | 0 | // |
255 | 0 | // For FunctionToCall::dispose, "from" and "to" point to the same state, and |
256 | 0 | // so this assignment logically would do nothing. |
257 | 0 | // |
258 | 0 | // Note: Correctness here relies on http://wg21.link/p0593, which has only |
259 | 0 | // become standard in C++20, though implementations do not break it in |
260 | 0 | // practice for earlier versions of C++. |
261 | 0 | // |
262 | 0 | // The correct way to do this without that paper is to first placement-new a |
263 | 0 | // default-constructed T in "to->storage" prior to the memmove, but doing so |
264 | 0 | // requires a different function to be created for each T that is stored |
265 | 0 | // locally, which can cause unnecessary bloat and be less cache friendly. |
266 | 0 | *to = *from; |
267 | 0 |
|
268 | 0 | // Note: Because the type is trivially copyable, the destructor does not need |
269 | 0 | // to be called ("trivially copyable" requires a trivial destructor). |
270 | 0 | } |
271 | | |
272 | | // The manager that is used when a target function is in local storage and is |
273 | | // not a trivially copyable type. |
274 | | template <class T> |
275 | | void LocalManagerNontrivial(FunctionToCall operation, |
276 | | TypeErasedState* const from, |
277 | | TypeErasedState* const to) noexcept { |
278 | | static_assert(IsStoredLocally<T>::value, |
279 | | "Local storage must only be used for supported types."); |
280 | | static_assert(!std::is_trivially_copyable<T>::value, |
281 | | "Locally stored types must be trivially copyable."); |
282 | | |
283 | | T& from_object = (ObjectInLocalStorage<T>)(from); |
284 | | |
285 | | switch (operation) { |
286 | | case FunctionToCall::relocate_from_to: |
287 | | // NOTE: Requires that the left-hand operand is already empty. |
288 | | ::new (static_cast<void*>(&to->storage)) T(std::move(from_object)); |
289 | | ABSL_FALLTHROUGH_INTENDED; |
290 | | case FunctionToCall::dispose: |
291 | | from_object.~T(); // Must not throw. // NOLINT |
292 | | return; |
293 | | } |
294 | | ABSL_UNREACHABLE(); |
295 | | } |
296 | | |
297 | | // The invoker that is used when a target function is in local storage |
298 | | // Note: QualTRef here is the target function type along with cv and reference |
299 | | // qualifiers that must be used when calling the function. |
300 | | template <bool SigIsNoexcept, class ReturnType, class QualTRef, class... P> |
301 | | ReturnType LocalInvoker( |
302 | | TypeErasedState* const state, |
303 | | ForwardedParameterType<P>... args) noexcept(SigIsNoexcept) { |
304 | | using RawT = RemoveCVRef<QualTRef>; |
305 | | static_assert( |
306 | | IsStoredLocally<RawT>::value, |
307 | | "Target object must be in local storage in order to be invoked from it."); |
308 | | |
309 | | auto& f = (ObjectInLocalStorage<RawT>)(state); |
310 | | return (InvokeR<ReturnType>)(static_cast<QualTRef>(f), |
311 | | static_cast<ForwardedParameterType<P>>(args)...); |
312 | | } |
313 | | |
314 | | // The manager that is used when a target function is in remote storage and it |
315 | | // has a trivial destructor |
316 | | inline void RemoteManagerTrivial(FunctionToCall operation, |
317 | | TypeErasedState* const from, |
318 | 0 | TypeErasedState* const to) noexcept { |
319 | 0 | switch (operation) { |
320 | 0 | case FunctionToCall::relocate_from_to: |
321 | 0 | // NOTE: Requires that the left-hand operand is already empty. |
322 | 0 | to->remote = from->remote; |
323 | 0 | return; |
324 | 0 | case FunctionToCall::dispose: |
325 | 0 | #if defined(__cpp_sized_deallocation) |
326 | 0 | ::operator delete(from->remote.target, from->remote.size); |
327 | 0 | #else // __cpp_sized_deallocation |
328 | 0 | ::operator delete(from->remote.target); |
329 | 0 | #endif // __cpp_sized_deallocation |
330 | 0 | return; |
331 | 0 | } |
332 | 0 | ABSL_UNREACHABLE(); |
333 | 0 | } |
334 | | |
335 | | // The manager that is used when a target function is in remote storage and the |
336 | | // destructor of the type is not trivial |
337 | | template <class T> |
338 | | void RemoteManagerNontrivial(FunctionToCall operation, |
339 | | TypeErasedState* const from, |
340 | | TypeErasedState* const to) noexcept { |
341 | | static_assert(!IsStoredLocally<T>::value, |
342 | | "Remote storage must only be used for types that do not " |
343 | | "qualify for local storage."); |
344 | | |
345 | | switch (operation) { |
346 | | case FunctionToCall::relocate_from_to: |
347 | | // NOTE: Requires that the left-hand operand is already empty. |
348 | | to->remote.target = from->remote.target; |
349 | | return; |
350 | | case FunctionToCall::dispose: |
351 | | ::delete static_cast<T*>(from->remote.target); // Must not throw. |
352 | | return; |
353 | | } |
354 | | ABSL_UNREACHABLE(); |
355 | | } |
356 | | |
357 | | // The invoker that is used when a target function is in remote storage |
358 | | template <bool SigIsNoexcept, class ReturnType, class QualTRef, class... P> |
359 | | ReturnType RemoteInvoker( |
360 | | TypeErasedState* const state, |
361 | | ForwardedParameterType<P>... args) noexcept(SigIsNoexcept) { |
362 | | using RawT = RemoveCVRef<QualTRef>; |
363 | | static_assert(!IsStoredLocally<RawT>::value, |
364 | | "Target object must be in remote storage in order to be " |
365 | | "invoked from it."); |
366 | | |
367 | | auto& f = *static_cast<RawT*>(state->remote.target); |
368 | | return (InvokeR<ReturnType>)(static_cast<QualTRef>(f), |
369 | | static_cast<ForwardedParameterType<P>>(args)...); |
370 | | } |
371 | | |
372 | | //////////////////////////////////////////////////////////////////////////////// |
373 | | // |
374 | | // A metafunction that checks if a type T is an instantiation of |
375 | | // absl::in_place_type_t (needed for constructor constraints of AnyInvocable). |
376 | | template <class T> |
377 | | struct IsInPlaceType : std::false_type {}; |
378 | | |
379 | | template <class T> |
380 | | struct IsInPlaceType<absl::in_place_type_t<T>> : std::true_type {}; |
381 | | // |
382 | | //////////////////////////////////////////////////////////////////////////////// |
383 | | |
384 | | // A constructor name-tag used with CoreImpl (below) to request the |
385 | | // conversion-constructor. QualDecayedTRef is the decayed-type of the object to |
386 | | // wrap, along with the cv and reference qualifiers that must be applied when |
387 | | // performing an invocation of the wrapped object. |
388 | | template <class QualDecayedTRef> |
389 | | struct TypedConversionConstruct {}; |
390 | | |
391 | | // A helper base class for all core operations of AnyInvocable. Most notably, |
392 | | // this class creates the function call operator and constraint-checkers so that |
393 | | // the top-level class does not have to be a series of partial specializations. |
394 | | // |
395 | | // Note: This definition exists (as opposed to being a declaration) so that if |
396 | | // the user of the top-level template accidentally passes a template argument |
397 | | // that is not a function type, they will get a static_assert in AnyInvocable's |
398 | | // class body rather than an error stating that Impl is not defined. |
399 | | template <class Sig> |
400 | | class Impl {}; // Note: This is partially-specialized later. |
401 | | |
402 | | // A std::unique_ptr deleter that deletes memory allocated via ::operator new. |
403 | | #if defined(__cpp_sized_deallocation) |
404 | | class TrivialDeleter { |
405 | | public: |
406 | | explicit TrivialDeleter(std::size_t size) : size_(size) {} |
407 | | |
408 | | void operator()(void* target) const { |
409 | | ::operator delete(target, size_); |
410 | | } |
411 | | |
412 | | private: |
413 | | std::size_t size_; |
414 | | }; |
415 | | #else // __cpp_sized_deallocation |
416 | | class TrivialDeleter { |
417 | | public: |
418 | 0 | explicit TrivialDeleter(std::size_t) {} |
419 | | |
420 | 0 | void operator()(void* target) const { ::operator delete(target); } |
421 | | }; |
422 | | #endif // __cpp_sized_deallocation |
423 | | |
424 | | template <bool SigIsNoexcept, class ReturnType, class... P> |
425 | | class CoreImpl; |
426 | | |
427 | 0 | constexpr bool IsCompatibleConversion(void*, void*) { return false; } |
428 | | template <bool NoExceptSrc, bool NoExceptDest, class... T> |
429 | | constexpr bool IsCompatibleConversion(CoreImpl<NoExceptSrc, T...>*, |
430 | | CoreImpl<NoExceptDest, T...>*) { |
431 | | return !NoExceptDest || NoExceptSrc; |
432 | | } |
433 | | |
434 | | // A helper base class for all core operations of AnyInvocable that do not |
435 | | // depend on the cv/ref qualifiers of the function type. |
436 | | template <bool SigIsNoexcept, class ReturnType, class... P> |
437 | | class CoreImpl { |
438 | | public: |
439 | | using result_type = ReturnType; |
440 | | |
441 | | CoreImpl() noexcept : manager_(EmptyManager), invoker_(nullptr) {} |
442 | | |
443 | | enum class TargetType { |
444 | | kPointer, |
445 | | kCompatibleAnyInvocable, |
446 | | kIncompatibleAnyInvocable, |
447 | | kOther, |
448 | | }; |
449 | | |
450 | | // Note: QualDecayedTRef here includes the cv-ref qualifiers associated with |
451 | | // the invocation of the Invocable. The unqualified type is the target object |
452 | | // type to be stored. |
453 | | template <class QualDecayedTRef, class F> |
454 | | explicit CoreImpl(TypedConversionConstruct<QualDecayedTRef>, F&& f) { |
455 | | using DecayedT = RemoveCVRef<QualDecayedTRef>; |
456 | | |
457 | | constexpr TargetType kTargetType = |
458 | | (std::is_pointer<DecayedT>::value || |
459 | | std::is_member_pointer<DecayedT>::value) |
460 | | ? TargetType::kPointer |
461 | | : IsCompatibleAnyInvocable<DecayedT>::value |
462 | | ? TargetType::kCompatibleAnyInvocable |
463 | | : IsAnyInvocable<DecayedT>::value |
464 | | ? TargetType::kIncompatibleAnyInvocable |
465 | | : TargetType::kOther; |
466 | | // NOTE: We only use integers instead of enums as template parameters in |
467 | | // order to work around a bug on C++14 under MSVC 2017. |
468 | | // See b/236131881. |
469 | | Initialize<kTargetType, QualDecayedTRef>(std::forward<F>(f)); |
470 | | } |
471 | | |
472 | | // Note: QualTRef here includes the cv-ref qualifiers associated with the |
473 | | // invocation of the Invocable. The unqualified type is the target object |
474 | | // type to be stored. |
475 | | template <class QualTRef, class... Args> |
476 | | explicit CoreImpl(absl::in_place_type_t<QualTRef>, Args&&... args) { |
477 | | InitializeStorage<QualTRef>(std::forward<Args>(args)...); |
478 | | } |
479 | | |
480 | | CoreImpl(CoreImpl&& other) noexcept { |
481 | | other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); |
482 | | manager_ = other.manager_; |
483 | | invoker_ = other.invoker_; |
484 | | other.manager_ = EmptyManager; |
485 | | other.invoker_ = nullptr; |
486 | | } |
487 | | |
488 | | CoreImpl& operator=(CoreImpl&& other) noexcept { |
489 | | // Put the left-hand operand in an empty state. |
490 | | // |
491 | | // Note: A full reset that leaves us with an object that has its invariants |
492 | | // intact is necessary in order to handle self-move. This is required by |
493 | | // types that are used with certain operations of the standard library, such |
494 | | // as the default definition of std::swap when both operands target the same |
495 | | // object. |
496 | | Clear(); |
497 | | |
498 | | // Perform the actual move/destroy operation on the target function. |
499 | | other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); |
500 | | manager_ = other.manager_; |
501 | | invoker_ = other.invoker_; |
502 | | other.manager_ = EmptyManager; |
503 | | other.invoker_ = nullptr; |
504 | | |
505 | | return *this; |
506 | | } |
507 | | |
508 | | ~CoreImpl() { manager_(FunctionToCall::dispose, &state_, &state_); } |
509 | | |
510 | | // Check whether or not the AnyInvocable is in the empty state. |
511 | | bool HasValue() const { return invoker_ != nullptr; } |
512 | | |
513 | | // Effects: Puts the object into its empty state. |
514 | | void Clear() { |
515 | | manager_(FunctionToCall::dispose, &state_, &state_); |
516 | | manager_ = EmptyManager; |
517 | | invoker_ = nullptr; |
518 | | } |
519 | | |
520 | | template <TargetType target_type, class QualDecayedTRef, class F, |
521 | | absl::enable_if_t<target_type == TargetType::kPointer, int> = 0> |
522 | | void Initialize(F&& f) { |
523 | | // This condition handles types that decay into pointers, which includes |
524 | | // function references. Since function references cannot be null, GCC warns |
525 | | // against comparing their decayed form with nullptr. |
526 | | // Since this is template-heavy code, we prefer to disable these warnings |
527 | | // locally instead of adding yet another overload of this function. |
528 | | #if !defined(__clang__) && defined(__GNUC__) |
529 | | #pragma GCC diagnostic push |
530 | | #pragma GCC diagnostic ignored "-Wpragmas" |
531 | | #pragma GCC diagnostic ignored "-Waddress" |
532 | | #pragma GCC diagnostic ignored "-Wnonnull-compare" |
533 | | #endif |
534 | | if (static_cast<RemoveCVRef<QualDecayedTRef>>(f) == nullptr) { |
535 | | #if !defined(__clang__) && defined(__GNUC__) |
536 | | #pragma GCC diagnostic pop |
537 | | #endif |
538 | | manager_ = EmptyManager; |
539 | | invoker_ = nullptr; |
540 | | return; |
541 | | } |
542 | | InitializeStorage<QualDecayedTRef>(std::forward<F>(f)); |
543 | | } |
544 | | |
545 | | template <TargetType target_type, class QualDecayedTRef, class F, |
546 | | absl::enable_if_t< |
547 | | target_type == TargetType::kCompatibleAnyInvocable, int> = 0> |
548 | | void Initialize(F&& f) { |
549 | | // In this case we can "steal the guts" of the other AnyInvocable. |
550 | | f.manager_(FunctionToCall::relocate_from_to, &f.state_, &state_); |
551 | | manager_ = f.manager_; |
552 | | invoker_ = f.invoker_; |
553 | | |
554 | | f.manager_ = EmptyManager; |
555 | | f.invoker_ = nullptr; |
556 | | } |
557 | | |
558 | | template <TargetType target_type, class QualDecayedTRef, class F, |
559 | | absl::enable_if_t< |
560 | | target_type == TargetType::kIncompatibleAnyInvocable, int> = 0> |
561 | | void Initialize(F&& f) { |
562 | | if (f.HasValue()) { |
563 | | InitializeStorage<QualDecayedTRef>(std::forward<F>(f)); |
564 | | } else { |
565 | | manager_ = EmptyManager; |
566 | | invoker_ = nullptr; |
567 | | } |
568 | | } |
569 | | |
570 | | template <TargetType target_type, class QualDecayedTRef, class F, |
571 | | typename = absl::enable_if_t<target_type == TargetType::kOther>> |
572 | | void Initialize(F&& f) { |
573 | | InitializeStorage<QualDecayedTRef>(std::forward<F>(f)); |
574 | | } |
575 | | |
576 | | // Use local (inline) storage for applicable target object types. |
577 | | template <class QualTRef, class... Args, |
578 | | typename = absl::enable_if_t< |
579 | | IsStoredLocally<RemoveCVRef<QualTRef>>::value>> |
580 | | void InitializeStorage(Args&&... args) { |
581 | | using RawT = RemoveCVRef<QualTRef>; |
582 | | ::new (static_cast<void*>(&state_.storage)) |
583 | | RawT(std::forward<Args>(args)...); |
584 | | |
585 | | invoker_ = LocalInvoker<SigIsNoexcept, ReturnType, QualTRef, P...>; |
586 | | // We can simplify our manager if we know the type is trivially copyable. |
587 | | InitializeLocalManager<RawT>(); |
588 | | } |
589 | | |
590 | | // Use remote storage for target objects that cannot be stored locally. |
591 | | template <class QualTRef, class... Args, |
592 | | absl::enable_if_t<!IsStoredLocally<RemoveCVRef<QualTRef>>::value, |
593 | | int> = 0> |
594 | | void InitializeStorage(Args&&... args) { |
595 | | InitializeRemoteManager<RemoveCVRef<QualTRef>>(std::forward<Args>(args)...); |
596 | | // This is set after everything else in case an exception is thrown in an |
597 | | // earlier step of the initialization. |
598 | | invoker_ = RemoteInvoker<SigIsNoexcept, ReturnType, QualTRef, P...>; |
599 | | } |
600 | | |
601 | | template <class T, |
602 | | typename = absl::enable_if_t<std::is_trivially_copyable<T>::value>> |
603 | | void InitializeLocalManager() { |
604 | | manager_ = LocalManagerTrivial; |
605 | | } |
606 | | |
607 | | template <class T, |
608 | | absl::enable_if_t<!std::is_trivially_copyable<T>::value, int> = 0> |
609 | | void InitializeLocalManager() { |
610 | | manager_ = LocalManagerNontrivial<T>; |
611 | | } |
612 | | |
613 | | template <class T> |
614 | | using HasTrivialRemoteStorage = |
615 | | std::integral_constant<bool, std::is_trivially_destructible<T>::value && |
616 | | alignof(T) <= |
617 | | ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT>; |
618 | | |
619 | | template <class T, class... Args, |
620 | | typename = absl::enable_if_t<HasTrivialRemoteStorage<T>::value>> |
621 | | void InitializeRemoteManager(Args&&... args) { |
622 | | // unique_ptr is used for exception-safety in case construction throws. |
623 | | std::unique_ptr<void, TrivialDeleter> uninitialized_target( |
624 | | ::operator new(sizeof(T)), TrivialDeleter(sizeof(T))); |
625 | | ::new (uninitialized_target.get()) T(std::forward<Args>(args)...); |
626 | | state_.remote.target = uninitialized_target.release(); |
627 | | state_.remote.size = sizeof(T); |
628 | | manager_ = RemoteManagerTrivial; |
629 | | } |
630 | | |
631 | | template <class T, class... Args, |
632 | | absl::enable_if_t<!HasTrivialRemoteStorage<T>::value, int> = 0> |
633 | | void InitializeRemoteManager(Args&&... args) { |
634 | | state_.remote.target = ::new T(std::forward<Args>(args)...); |
635 | | manager_ = RemoteManagerNontrivial<T>; |
636 | | } |
637 | | |
638 | | ////////////////////////////////////////////////////////////////////////////// |
639 | | // |
640 | | // Type trait to determine if the template argument is an AnyInvocable whose |
641 | | // function type is compatible enough with ours such that we can |
642 | | // "move the guts" out of it when moving, rather than having to place a new |
643 | | // object into remote storage. |
644 | | |
645 | | template <typename Other> |
646 | | struct IsCompatibleAnyInvocable { |
647 | | static constexpr bool value = false; |
648 | | }; |
649 | | |
650 | | template <typename Sig> |
651 | | struct IsCompatibleAnyInvocable<AnyInvocable<Sig>> { |
652 | | static constexpr bool value = |
653 | | (IsCompatibleConversion)(static_cast< |
654 | | typename AnyInvocable<Sig>::CoreImpl*>( |
655 | | nullptr), |
656 | | static_cast<CoreImpl*>(nullptr)); |
657 | | }; |
658 | | |
659 | | // |
660 | | ////////////////////////////////////////////////////////////////////////////// |
661 | | |
662 | | TypeErasedState state_; |
663 | | ManagerType* manager_; |
664 | | InvokerType<SigIsNoexcept, ReturnType, P...>* invoker_; |
665 | | }; |
666 | | |
667 | | // A constructor name-tag used with Impl to request the |
668 | | // conversion-constructor |
669 | | struct ConversionConstruct {}; |
670 | | |
671 | | //////////////////////////////////////////////////////////////////////////////// |
672 | | // |
673 | | // A metafunction that is normally an identity metafunction except that when |
674 | | // given a std::reference_wrapper<T>, it yields T&. This is necessary because |
675 | | // currently std::reference_wrapper's operator() is not conditionally noexcept, |
676 | | // so when checking if such an Invocable is nothrow-invocable, we must pull out |
677 | | // the underlying type. |
678 | | template <class T> |
679 | | struct UnwrapStdReferenceWrapperImpl { |
680 | | using type = T; |
681 | | }; |
682 | | |
683 | | template <class T> |
684 | | struct UnwrapStdReferenceWrapperImpl<std::reference_wrapper<T>> { |
685 | | using type = T&; |
686 | | }; |
687 | | |
688 | | template <class T> |
689 | | using UnwrapStdReferenceWrapper = |
690 | | typename UnwrapStdReferenceWrapperImpl<T>::type; |
691 | | // |
692 | | //////////////////////////////////////////////////////////////////////////////// |
693 | | |
694 | | // An alias that always yields std::true_type (used with constraints) where |
695 | | // substitution failures happen when forming the template arguments. |
696 | | template <class... T> |
697 | | using TrueAlias = |
698 | | std::integral_constant<bool, sizeof(absl::void_t<T...>*) != 0>; |
699 | | |
700 | | /*SFINAE constraints for the conversion-constructor.*/ |
701 | | template <class Sig, class F, |
702 | | class = absl::enable_if_t< |
703 | | !std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>> |
704 | | using CanConvert = TrueAlias< |
705 | | absl::enable_if_t<!IsInPlaceType<RemoveCVRef<F>>::value>, |
706 | | absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, |
707 | | absl::enable_if_t< |
708 | | Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, |
709 | | absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>; |
710 | | |
711 | | /*SFINAE constraints for the std::in_place constructors.*/ |
712 | | template <class Sig, class F, class... Args> |
713 | | using CanEmplace = TrueAlias< |
714 | | absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, |
715 | | absl::enable_if_t< |
716 | | Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, |
717 | | absl::enable_if_t<std::is_constructible<absl::decay_t<F>, Args...>::value>>; |
718 | | |
719 | | /*SFINAE constraints for the conversion-assign operator.*/ |
720 | | template <class Sig, class F, |
721 | | class = absl::enable_if_t< |
722 | | !std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>> |
723 | | using CanAssign = TrueAlias< |
724 | | absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>, |
725 | | absl::enable_if_t< |
726 | | Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>, |
727 | | absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>; |
728 | | |
729 | | /*SFINAE constraints for the reference-wrapper conversion-assign operator.*/ |
730 | | template <class Sig, class F> |
731 | | using CanAssignReferenceWrapper = TrueAlias< |
732 | | absl::enable_if_t< |
733 | | Impl<Sig>::template CallIsValid<std::reference_wrapper<F>>::value>, |
734 | | absl::enable_if_t<Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept< |
735 | | std::reference_wrapper<F>>::value>>; |
736 | | |
737 | | //////////////////////////////////////////////////////////////////////////////// |
738 | | // |
739 | | // The constraint for checking whether or not a call meets the noexcept |
740 | | // callability requirements. This is a preprocessor macro because specifying it |
741 | | // this way as opposed to a disjunction/branch can improve the user-side error |
742 | | // messages and avoids an instantiation of std::is_nothrow_invocable_r in the |
743 | | // cases where the user did not specify a noexcept function type. |
744 | | // |
745 | | #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT(inv_quals, noex) \ |
746 | | ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_##noex(inv_quals) |
747 | | |
748 | | // The disjunction below is because we can't rely on std::is_nothrow_invocable_r |
749 | | // to give the right result when ReturnType is non-moveable in toolchains that |
750 | | // don't treat non-moveable result types correctly. For example this was the |
751 | | // case in libc++ before commit c3a24882 (2022-05). |
752 | | #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true(inv_quals) \ |
753 | | absl::enable_if_t<absl::disjunction< \ |
754 | | std::is_nothrow_invocable_r< \ |
755 | | ReturnType, UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \ |
756 | | P...>, \ |
757 | | std::conjunction< \ |
758 | | std::is_nothrow_invocable< \ |
759 | | UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, P...>, \ |
760 | | std::is_same< \ |
761 | | ReturnType, \ |
762 | | absl::base_internal::invoke_result_t< \ |
763 | | UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \ |
764 | | P...>>>>::value> |
765 | | |
766 | | #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false(inv_quals) |
767 | | // |
768 | | //////////////////////////////////////////////////////////////////////////////// |
769 | | |
770 | | // A macro to generate partial specializations of Impl with the different |
771 | | // combinations of supported cv/reference qualifiers and noexcept specifier. |
772 | | // |
773 | | // Here, `cv` are the cv-qualifiers if any, `ref` is the ref-qualifier if any, |
774 | | // inv_quals is the reference type to be used when invoking the target, and |
775 | | // noex is "true" if the function type is noexcept, or false if it is not. |
776 | | // |
777 | | // The CallIsValid condition is more complicated than simply using |
778 | | // absl::base_internal::is_invocable_r because we can't rely on it to give the |
779 | | // right result when ReturnType is non-moveable in toolchains that don't treat |
780 | | // non-moveable result types correctly. For example this was the case in libc++ |
781 | | // before commit c3a24882 (2022-05). |
782 | | #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, noex) \ |
783 | | template <class ReturnType, class... P> \ |
784 | | class Impl<ReturnType(P...) cv ref ABSL_INTERNAL_NOEXCEPT_SPEC(noex)> \ |
785 | | : public CoreImpl<noex, ReturnType, P...> { \ |
786 | | public: \ |
787 | | /*The base class, which contains the datamembers and core operations*/ \ |
788 | | using Core = CoreImpl<noex, ReturnType, P...>; \ |
789 | | \ |
790 | | /*SFINAE constraint to check if F is invocable with the proper signature*/ \ |
791 | | template <class F> \ |
792 | | using CallIsValid = TrueAlias<absl::enable_if_t<absl::disjunction< \ |
793 | | absl::base_internal::is_invocable_r<ReturnType, \ |
794 | | absl::decay_t<F> inv_quals, P...>, \ |
795 | | std::is_same<ReturnType, \ |
796 | | absl::base_internal::invoke_result_t< \ |
797 | | absl::decay_t<F> inv_quals, P...>>>::value>>; \ |
798 | | \ |
799 | | /*SFINAE constraint to check if F is nothrow-invocable when necessary*/ \ |
800 | | template <class F> \ |
801 | | using CallIsNoexceptIfSigIsNoexcept = \ |
802 | | TrueAlias<ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT(inv_quals, \ |
803 | | noex)>; \ |
804 | | \ |
805 | | /*Put the AnyInvocable into an empty state.*/ \ |
806 | | Impl() = default; \ |
807 | | \ |
808 | | /*The implementation of a conversion-constructor from "f*/ \ |
809 | | /*This forwards to Core, attaching inv_quals so that the base class*/ \ |
810 | | /*knows how to properly type-erase the invocation.*/ \ |
811 | | template <class F> \ |
812 | | explicit Impl(ConversionConstruct, F&& f) \ |
813 | | : Core(TypedConversionConstruct< \ |
814 | | typename std::decay<F>::type inv_quals>(), \ |
815 | | std::forward<F>(f)) {} \ |
816 | | \ |
817 | | /*Forward along the in-place construction parameters.*/ \ |
818 | | template <class T, class... Args> \ |
819 | | explicit Impl(absl::in_place_type_t<T>, Args&&... args) \ |
820 | | : Core(absl::in_place_type<absl::decay_t<T> inv_quals>, \ |
821 | | std::forward<Args>(args)...) {} \ |
822 | | \ |
823 | | /*Raises a fatal error when the AnyInvocable is invoked after a move*/ \ |
824 | | static ReturnType InvokedAfterMove( \ |
825 | | TypeErasedState*, \ |
826 | | ForwardedParameterType<P>...) noexcept(noex) { \ |
827 | | ABSL_HARDENING_ASSERT(false && "AnyInvocable use-after-move"); \ |
828 | | std::terminate(); \ |
829 | | } \ |
830 | | \ |
831 | | InvokerType<noex, ReturnType, P...>* ExtractInvoker() cv { \ |
832 | | using QualifiedTestType = int cv ref; \ |
833 | | auto* invoker = this->invoker_; \ |
834 | | if (!std::is_const<QualifiedTestType>::value && \ |
835 | | std::is_rvalue_reference<QualifiedTestType>::value) { \ |
836 | | ABSL_ASSERT([this]() { \ |
837 | | /* We checked that this isn't const above, so const_cast is safe */ \ |
838 | | const_cast<Impl*>(this)->invoker_ = InvokedAfterMove; \ |
839 | | return this->HasValue(); \ |
840 | | }()); \ |
841 | | } \ |
842 | | return invoker; \ |
843 | | } \ |
844 | | \ |
845 | | /*The actual invocation operation with the proper signature*/ \ |
846 | | ReturnType operator()(P... args) cv ref noexcept(noex) { \ |
847 | | assert(this->invoker_ != nullptr); \ |
848 | | return this->ExtractInvoker()( \ |
849 | | const_cast<TypeErasedState*>(&this->state_), \ |
850 | | static_cast<ForwardedParameterType<P>>(args)...); \ |
851 | | } \ |
852 | | } |
853 | | |
854 | | // Define the `noexcept(true)` specialization only for C++17 and beyond, when |
855 | | // `noexcept` is part of the type system. |
856 | | #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L |
857 | | // A convenience macro that defines specializations for the noexcept(true) and |
858 | | // noexcept(false) forms, given the other properties. |
859 | | #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ |
860 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false); \ |
861 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, true) |
862 | | #else |
863 | | #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ |
864 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false) |
865 | | #endif |
866 | | |
867 | | // Non-ref-qualified partial specializations |
868 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, , &); |
869 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, , const&); |
870 | | |
871 | | // Lvalue-ref-qualified partial specializations |
872 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &, &); |
873 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &, const&); |
874 | | |
875 | | // Rvalue-ref-qualified partial specializations |
876 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &&, &&); |
877 | | ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &&, const&&); |
878 | | |
879 | | // Undef the detail-only macros. |
880 | | #undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL |
881 | | #undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL_ |
882 | | #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false |
883 | | #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true |
884 | | #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT |
885 | | #undef ABSL_INTERNAL_NOEXCEPT_SPEC |
886 | | |
887 | | } // namespace internal_any_invocable |
888 | | ABSL_NAMESPACE_END |
889 | | } // namespace absl |
890 | | |
891 | | #endif // ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ |