// Copyright 2022 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Implementation details for `absl::AnyInvocable` #ifndef ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ #define ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ //////////////////////////////////////////////////////////////////////////////// // // // This implementation chooses between local storage and remote storage for // // the contained target object based on the target object's size, alignment // // requirements, and whether or not it has a nothrow move constructor. // // Additional optimizations are performed when the object is a trivially // // copyable type [basic.types]. // // // // There are three datamembers per `AnyInvocable` instance // // // // 1) A union containing either // // - A pointer to the target object referred to via a void*, or // // - the target object, emplaced into a raw char buffer // // // // 2) A function pointer to a "manager" function operation that takes a // // discriminator and logically branches to either perform a move operation // // or destroy operation based on that discriminator. // // // // 3) A function pointer to an "invoker" function operation that invokes the // // target object, directly returning the result. // // // // When in the logically empty state, the manager function is an empty // // function and the invoker function is one that would be undefined behavior // // to call. // // // // An additional optimization is performed when converting from one // // AnyInvocable to another where only the noexcept specification and/or the // // cv/ref qualifiers of the function type differ. In these cases, the // // conversion works by "moving the guts", similar to if they were the same // // exact type, as opposed to having to perform an additional layer of // // wrapping through remote storage. // // // //////////////////////////////////////////////////////////////////////////////// // IWYU pragma: private, include "absl/functional/any_invocable.h" #include <cassert> #include <cstddef> #include <cstring> #include <exception> #include <functional> #include <memory> #include <new> #include <type_traits> #include <utility> #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/invoke.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/meta/type_traits.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN // Helper macro used to prevent spelling `noexcept` in language versions older // than C++17, where it is not part of the type system, in order to avoid // compilation failures and internal compiler errors. #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L #define ABSL_INTERNAL_NOEXCEPT_SPEC … #else #define ABSL_INTERNAL_NOEXCEPT_SPEC … #endif // Defined in functional/any_invocable.h template <class Sig> class AnyInvocable; namespace internal_any_invocable { // Constants relating to the small-object-storage for AnyInvocable enum StorageProperty : std::size_t { … }; //////////////////////////////////////////////////////////////////////////////// // // A metafunction for checking if a type is an AnyInvocable instantiation. // This is used during conversion operations. template <class T> struct IsAnyInvocable : std::false_type { … }; IsAnyInvocable<AnyInvocable<Sig>>; // //////////////////////////////////////////////////////////////////////////////// // A type trait that tells us whether or not a target function type should be // stored locally in the small object optimization storage IsStoredLocally; // An implementation of std::remove_cvref_t of C++20. RemoveCVRef; //////////////////////////////////////////////////////////////////////////////// // // An implementation of the C++ standard INVOKE<R> pseudo-macro, operation is // equivalent to std::invoke except that it forces an implicit conversion to the // specified return type. If "R" is void, the function is executed and the // return value is simply ignored. template <class ReturnType, class F, class... P, typename = absl::enable_if_t<std::is_void<ReturnType>::value>> void InvokeR(F&& f, P&&... args) { … } template <class ReturnType, class F, class... P, absl::enable_if_t<!std::is_void<ReturnType>::value, int> = 0> ReturnType InvokeR(F&& f, P&&... args) { … } // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// // A metafunction that takes a "T" corresponding to a parameter type of the // user's specified function type, and yields the parameter type to use for the // type-erased invoker. In order to prevent observable moves, this must be // either a reference or, if the type is trivial, the original parameter type // itself. Since the parameter type may be incomplete at the point that this // metafunction is used, we can only do this optimization for scalar types // rather than for any trivial type. template <typename T> T ForwardImpl(std::true_type); template <typename T> T&& ForwardImpl(std::false_type); // NOTE: We deliberately use an intermediate struct instead of a direct alias, // as a workaround for b/206991861 on MSVC versions < 1924. template <class T> struct ForwardedParameter { … }; ForwardedParameterType; // //////////////////////////////////////////////////////////////////////////////// // A discriminator when calling the "manager" function that describes operation // type-erased operation should be invoked. // // "relocate_from_to" specifies that the manager should perform a move. // // "dispose" specifies that the manager should perform a destroy. enum class FunctionToCall : bool { … }; // The portion of `AnyInvocable` state that contains either a pointer to the // target object or the object itself in local storage TypeErasedState; // A typed accessor for the object in `TypeErasedState` storage template <class T> T& ObjectInLocalStorage(TypeErasedState* const state) { … } // The type for functions issuing lifetime-related operations: move and dispose // A pointer to such a function is contained in each `AnyInvocable` instance. // NOTE: When specifying `FunctionToCall::`dispose, the same state must be // passed as both "from" and "to". ManagerType; // The type for functions issuing the actual invocation of the object // A pointer to such a function is contained in each AnyInvocable instance. InvokerType; // The manager that is used when AnyInvocable is empty inline void EmptyManager(FunctionToCall /*operation*/, TypeErasedState* /*from*/, TypeErasedState* /*to*/) noexcept { … } // The manager that is used when a target function is in local storage and is // a trivially copyable type. inline void LocalManagerTrivial(FunctionToCall /*operation*/, TypeErasedState* const from, TypeErasedState* const to) noexcept { … } // The manager that is used when a target function is in local storage and is // not a trivially copyable type. template <class T> void LocalManagerNontrivial(FunctionToCall operation, TypeErasedState* const from, TypeErasedState* const to) noexcept { … } // The invoker that is used when a target function is in local storage // Note: QualTRef here is the target function type along with cv and reference // qualifiers that must be used when calling the function. template <bool SigIsNoexcept, class ReturnType, class QualTRef, class... P> ReturnType LocalInvoker( TypeErasedState* const state, ForwardedParameterType<P>... args) noexcept(SigIsNoexcept) { … } // The manager that is used when a target function is in remote storage and it // has a trivial destructor inline void RemoteManagerTrivial(FunctionToCall operation, TypeErasedState* const from, TypeErasedState* const to) noexcept { … } // The manager that is used when a target function is in remote storage and the // destructor of the type is not trivial template <class T> void RemoteManagerNontrivial(FunctionToCall operation, TypeErasedState* const from, TypeErasedState* const to) noexcept { … } // The invoker that is used when a target function is in remote storage template <bool SigIsNoexcept, class ReturnType, class QualTRef, class... P> ReturnType RemoteInvoker( TypeErasedState* const state, ForwardedParameterType<P>... args) noexcept(SigIsNoexcept) { … } //////////////////////////////////////////////////////////////////////////////// // // A metafunction that checks if a type T is an instantiation of // absl::in_place_type_t (needed for constructor constraints of AnyInvocable). template <class T> struct IsInPlaceType : std::false_type { … }; IsInPlaceType<absl::in_place_type_t<T>>; // //////////////////////////////////////////////////////////////////////////////// // A constructor name-tag used with CoreImpl (below) to request the // conversion-constructor. QualDecayedTRef is the decayed-type of the object to // wrap, along with the cv and reference qualifiers that must be applied when // performing an invocation of the wrapped object. template <class QualDecayedTRef> struct TypedConversionConstruct { … }; // A helper base class for all core operations of AnyInvocable. Most notably, // this class creates the function call operator and constraint-checkers so that // the top-level class does not have to be a series of partial specializations. // // Note: This definition exists (as opposed to being a declaration) so that if // the user of the top-level template accidentally passes a template argument // that is not a function type, they will get a static_assert in AnyInvocable's // class body rather than an error stating that Impl is not defined. template <class Sig> class Impl { … }; // Note: This is partially-specialized later. // A std::unique_ptr deleter that deletes memory allocated via ::operator new. #if defined(__cpp_sized_deallocation) class TrivialDeleter { public: explicit TrivialDeleter(std::size_t size) : size_(size) {} void operator()(void* target) const { ::operator delete(target, size_); } private: std::size_t size_; }; #else // __cpp_sized_deallocation class TrivialDeleter { … }; #endif // __cpp_sized_deallocation template <bool SigIsNoexcept, class ReturnType, class... P> class CoreImpl; constexpr bool IsCompatibleConversion(void*, void*) { … } template <bool NoExceptSrc, bool NoExceptDest, class... T> constexpr bool IsCompatibleConversion(CoreImpl<NoExceptSrc, T...>*, CoreImpl<NoExceptDest, T...>*) { … } // A helper base class for all core operations of AnyInvocable that do not // depend on the cv/ref qualifiers of the function type. template <bool SigIsNoexcept, class ReturnType, class... P> class CoreImpl { … }; // A constructor name-tag used with Impl to request the // conversion-constructor struct ConversionConstruct { … }; //////////////////////////////////////////////////////////////////////////////// // // A metafunction that is normally an identity metafunction except that when // given a std::reference_wrapper<T>, it yields T&. This is necessary because // currently std::reference_wrapper's operator() is not conditionally noexcept, // so when checking if such an Invocable is nothrow-invocable, we must pull out // the underlying type. template <class T> struct UnwrapStdReferenceWrapperImpl { … }; UnwrapStdReferenceWrapperImpl<std::reference_wrapper<T>>; UnwrapStdReferenceWrapper; // //////////////////////////////////////////////////////////////////////////////// // An alias that always yields std::true_type (used with constraints) where // substitution failures happen when forming the template arguments. TrueAlias; /*SFINAE constraints for the conversion-constructor.*/ CanConvert; /*SFINAE constraints for the std::in_place constructors.*/ CanEmplace; /*SFINAE constraints for the conversion-assign operator.*/ CanAssign; /*SFINAE constraints for the reference-wrapper conversion-assign operator.*/ CanAssignReferenceWrapper; //////////////////////////////////////////////////////////////////////////////// // // The constraint for checking whether or not a call meets the noexcept // callability requirements. This is a preprocessor macro because specifying it // this way as opposed to a disjunction/branch can improve the user-side error // messages and avoids an instantiation of std::is_nothrow_invocable_r in the // cases where the user did not specify a noexcept function type. // #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT … // The disjunction below is because we can't rely on std::is_nothrow_invocable_r // to give the right result when ReturnType is non-moveable in toolchains that // don't treat non-moveable result types correctly. For example this was the // case in libc++ before commit c3a24882 (2022-05). #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true … #define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false … // //////////////////////////////////////////////////////////////////////////////// // A macro to generate partial specializations of Impl with the different // combinations of supported cv/reference qualifiers and noexcept specifier. // // Here, `cv` are the cv-qualifiers if any, `ref` is the ref-qualifier if any, // inv_quals is the reference type to be used when invoking the target, and // noex is "true" if the function type is noexcept, or false if it is not. // // The CallIsValid condition is more complicated than simply using // absl::base_internal::is_invocable_r because we can't rely on it to give the // right result when ReturnType is non-moveable in toolchains that don't treat // non-moveable result types correctly. For example this was the case in libc++ // before commit c3a24882 (2022-05). #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL_ … // Define the `noexcept(true)` specialization only for C++17 and beyond, when // `noexcept` is part of the type system. #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L // A convenience macro that defines specializations for the noexcept(true) and // noexcept(false) forms, given the other properties. #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL … #else #define ABSL_INTERNAL_ANY_INVOCABLE_IMPL … #endif // Non-ref-qualified partial specializations ABSL_INTERNAL_ANY_INVOCABLE_IMPL; ABSL_INTERNAL_ANY_INVOCABLE_IMPL; // Lvalue-ref-qualified partial specializations ABSL_INTERNAL_ANY_INVOCABLE_IMPL; ABSL_INTERNAL_ANY_INVOCABLE_IMPL; // Rvalue-ref-qualified partial specializations ABSL_INTERNAL_ANY_INVOCABLE_IMPL; ABSL_INTERNAL_ANY_INVOCABLE_IMPL; // Undef the detail-only macros. #undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL #undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL_ #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true #undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT #undef ABSL_INTERNAL_NOEXCEPT_SPEC } // namespace internal_any_invocable ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_