Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,855 @@
|
|||
// 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(noex) noexcept(noex)
|
||||
#else
|
||||
#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex)
|
||||
#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 {
|
||||
kAlignment = alignof(std::max_align_t), // The alignment of the storage
|
||||
kStorageSize = sizeof(void*) * 2 // The size of the storage
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 {};
|
||||
|
||||
template <class Sig>
|
||||
struct IsAnyInvocable<AnyInvocable<Sig>> : std::true_type {};
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A metafunction that tells us whether or not a target function type should be
|
||||
// stored locally in the small object optimization storage
|
||||
template <class T>
|
||||
constexpr bool IsStoredLocally() {
|
||||
if constexpr (sizeof(T) <= kStorageSize && alignof(T) <= kAlignment &&
|
||||
kAlignment % alignof(T) == 0) {
|
||||
return std::is_nothrow_move_constructible<T>::value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An implementation of std::remove_cvref_t of C++20.
|
||||
template <class T>
|
||||
using RemoveCVRef =
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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) {
|
||||
absl::base_internal::invoke(std::forward<F>(f), std::forward<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) {
|
||||
// GCC 12 has a false-positive -Wmaybe-uninitialized warning here.
|
||||
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
return absl::base_internal::invoke(std::forward<F>(f),
|
||||
std::forward<P>(args)...);
|
||||
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
// 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 {
|
||||
using type = decltype((
|
||||
ForwardImpl<T>)(std::integral_constant<bool,
|
||||
std::is_scalar<T>::value>()));
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using ForwardedParameterType = typename ForwardedParameter<T>::type;
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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 { relocate_from_to, dispose };
|
||||
|
||||
// The portion of `AnyInvocable` state that contains either a pointer to the
|
||||
// target object or the object itself in local storage
|
||||
union TypeErasedState {
|
||||
struct {
|
||||
// A pointer to the type-erased object when remotely stored
|
||||
void* target;
|
||||
// The size of the object for `RemoteManagerTrivial`
|
||||
std::size_t size;
|
||||
} remote;
|
||||
|
||||
// Local-storage for the type-erased object when small and trivial enough
|
||||
alignas(kAlignment) char storage[kStorageSize];
|
||||
};
|
||||
|
||||
// A typed accessor for the object in `TypeErasedState` storage
|
||||
template <class T>
|
||||
T& ObjectInLocalStorage(TypeErasedState* const state) {
|
||||
// We launder here because the storage may be reused with the same type.
|
||||
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
|
||||
return *std::launder(reinterpret_cast<T*>(&state->storage));
|
||||
#elif ABSL_HAVE_BUILTIN(__builtin_launder)
|
||||
return *__builtin_launder(reinterpret_cast<T*>(&state->storage));
|
||||
#else
|
||||
|
||||
// When `std::launder` or equivalent are not available, we rely on undefined
|
||||
// behavior, which works as intended on Abseil's officially supported
|
||||
// platforms as of Q2 2022.
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
return *reinterpret_cast<T*>(&state->storage);
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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".
|
||||
using ManagerType = void(FunctionToCall /*operation*/,
|
||||
TypeErasedState* /*from*/, TypeErasedState* /*to*/)
|
||||
ABSL_INTERNAL_NOEXCEPT_SPEC(true);
|
||||
|
||||
// The type for functions issuing the actual invocation of the object
|
||||
// A pointer to such a function is contained in each AnyInvocable instance.
|
||||
template <bool SigIsNoexcept, class ReturnType, class... P>
|
||||
using InvokerType = ReturnType(TypeErasedState*, ForwardedParameterType<P>...)
|
||||
ABSL_INTERNAL_NOEXCEPT_SPEC(SigIsNoexcept);
|
||||
|
||||
// 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 {
|
||||
// This single statement without branching handles both possible operations.
|
||||
//
|
||||
// For FunctionToCall::dispose, "from" and "to" point to the same state, and
|
||||
// so this assignment logically would do nothing.
|
||||
//
|
||||
// Note: Correctness here relies on http://wg21.link/p0593, which has only
|
||||
// become standard in C++20, though implementations do not break it in
|
||||
// practice for earlier versions of C++.
|
||||
//
|
||||
// The correct way to do this without that paper is to first placement-new a
|
||||
// default-constructed T in "to->storage" prior to the memmove, but doing so
|
||||
// requires a different function to be created for each T that is stored
|
||||
// locally, which can cause unnecessary bloat and be less cache friendly.
|
||||
*to = *from;
|
||||
|
||||
// Note: Because the type is trivially copyable, the destructor does not need
|
||||
// to be called ("trivially copyable" requires a trivial destructor).
|
||||
}
|
||||
|
||||
// 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 {
|
||||
static_assert(IsStoredLocally<T>(),
|
||||
"Local storage must only be used for supported types.");
|
||||
static_assert(!std::is_trivially_copyable<T>::value,
|
||||
"Locally stored types must be trivially copyable.");
|
||||
|
||||
T& from_object = (ObjectInLocalStorage<T>)(from);
|
||||
|
||||
switch (operation) {
|
||||
case FunctionToCall::relocate_from_to:
|
||||
// NOTE: Requires that the left-hand operand is already empty.
|
||||
::new (static_cast<void*>(&to->storage)) T(std::move(from_object));
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
case FunctionToCall::dispose:
|
||||
from_object.~T(); // Must not throw. // NOLINT
|
||||
return;
|
||||
}
|
||||
ABSL_UNREACHABLE();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
using RawT = RemoveCVRef<QualTRef>;
|
||||
static_assert(
|
||||
IsStoredLocally<RawT>(),
|
||||
"Target object must be in local storage in order to be invoked from it.");
|
||||
|
||||
auto& f = (ObjectInLocalStorage<RawT>)(state);
|
||||
return (InvokeR<ReturnType>)(static_cast<QualTRef>(f),
|
||||
static_cast<ForwardedParameterType<P>>(args)...);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch (operation) {
|
||||
case FunctionToCall::relocate_from_to:
|
||||
// NOTE: Requires that the left-hand operand is already empty.
|
||||
to->remote = from->remote;
|
||||
return;
|
||||
case FunctionToCall::dispose:
|
||||
#if defined(__cpp_sized_deallocation)
|
||||
::operator delete(from->remote.target, from->remote.size);
|
||||
#else // __cpp_sized_deallocation
|
||||
::operator delete(from->remote.target);
|
||||
#endif // __cpp_sized_deallocation
|
||||
return;
|
||||
}
|
||||
ABSL_UNREACHABLE();
|
||||
}
|
||||
|
||||
// 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 {
|
||||
static_assert(!IsStoredLocally<T>(),
|
||||
"Remote storage must only be used for types that do not "
|
||||
"qualify for local storage.");
|
||||
|
||||
switch (operation) {
|
||||
case FunctionToCall::relocate_from_to:
|
||||
// NOTE: Requires that the left-hand operand is already empty.
|
||||
to->remote.target = from->remote.target;
|
||||
return;
|
||||
case FunctionToCall::dispose:
|
||||
::delete static_cast<T*>(from->remote.target); // Must not throw.
|
||||
return;
|
||||
}
|
||||
ABSL_UNREACHABLE();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
using RawT = RemoveCVRef<QualTRef>;
|
||||
static_assert(!IsStoredLocally<RawT>(),
|
||||
"Target object must be in remote storage in order to be "
|
||||
"invoked from it.");
|
||||
|
||||
auto& f = *static_cast<RawT*>(state->remote.target);
|
||||
return (InvokeR<ReturnType>)(static_cast<QualTRef>(f),
|
||||
static_cast<ForwardedParameterType<P>>(args)...);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 {};
|
||||
|
||||
template <class T>
|
||||
struct IsInPlaceType<absl::in_place_type_t<T>> : std::true_type {};
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
explicit TrivialDeleter(std::size_t) {}
|
||||
|
||||
void operator()(void* target) const { ::operator delete(target); }
|
||||
};
|
||||
#endif // __cpp_sized_deallocation
|
||||
|
||||
template <bool SigIsNoexcept, class ReturnType, class... P>
|
||||
class CoreImpl;
|
||||
|
||||
constexpr bool IsCompatibleConversion(void*, void*) { return false; }
|
||||
template <bool NoExceptSrc, bool NoExceptDest, class... T>
|
||||
constexpr bool IsCompatibleConversion(CoreImpl<NoExceptSrc, T...>*,
|
||||
CoreImpl<NoExceptDest, T...>*) {
|
||||
return !NoExceptDest || NoExceptSrc;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
public:
|
||||
using result_type = ReturnType;
|
||||
|
||||
CoreImpl() noexcept : manager_(EmptyManager), invoker_(nullptr) {}
|
||||
|
||||
// Note: QualDecayedTRef here includes the cv-ref qualifiers associated with
|
||||
// the invocation of the Invocable. The unqualified type is the target object
|
||||
// type to be stored.
|
||||
template <class QualDecayedTRef, class F>
|
||||
explicit CoreImpl(TypedConversionConstruct<QualDecayedTRef>, F&& f) {
|
||||
using DecayedT = RemoveCVRef<QualDecayedTRef>;
|
||||
|
||||
if constexpr (std::is_pointer<DecayedT>::value ||
|
||||
std::is_member_pointer<DecayedT>::value) {
|
||||
// This condition handles types that decay into pointers, which includes
|
||||
// function references. Since function references cannot be null, GCC
|
||||
// warns against comparing their decayed form with nullptr. Since this is
|
||||
// template-heavy code, we prefer to disable these warnings locally
|
||||
// instead of adding yet another overload of this function.
|
||||
//
|
||||
// TODO(b/290784225): Avoid warnings using constexpr programming instead.
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||
#pragma GCC diagnostic ignored "-Waddress"
|
||||
#pragma GCC diagnostic ignored "-Wnonnull-compare"
|
||||
#endif
|
||||
if (static_cast<DecayedT>(f) == nullptr) {
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
manager_ = EmptyManager;
|
||||
invoker_ = nullptr;
|
||||
} else {
|
||||
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
|
||||
}
|
||||
} else if constexpr (IsCompatibleAnyInvocable<DecayedT>::value) {
|
||||
// In this case we can "steal the guts" of the other AnyInvocable.
|
||||
f.manager_(FunctionToCall::relocate_from_to, &f.state_, &state_);
|
||||
manager_ = f.manager_;
|
||||
invoker_ = f.invoker_;
|
||||
|
||||
f.manager_ = EmptyManager;
|
||||
f.invoker_ = nullptr;
|
||||
} else if constexpr (IsAnyInvocable<DecayedT>::value) {
|
||||
if (f.HasValue()) {
|
||||
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
|
||||
} else {
|
||||
manager_ = EmptyManager;
|
||||
invoker_ = nullptr;
|
||||
}
|
||||
} else {
|
||||
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
// Note: QualTRef here includes the cv-ref qualifiers associated with the
|
||||
// invocation of the Invocable. The unqualified type is the target object
|
||||
// type to be stored.
|
||||
template <class QualTRef, class... Args>
|
||||
explicit CoreImpl(absl::in_place_type_t<QualTRef>, Args&&... args) {
|
||||
InitializeStorage<QualTRef>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
CoreImpl(CoreImpl&& other) noexcept {
|
||||
other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_);
|
||||
manager_ = other.manager_;
|
||||
invoker_ = other.invoker_;
|
||||
other.manager_ = EmptyManager;
|
||||
other.invoker_ = nullptr;
|
||||
}
|
||||
|
||||
CoreImpl& operator=(CoreImpl&& other) noexcept {
|
||||
// Put the left-hand operand in an empty state.
|
||||
//
|
||||
// Note: A full reset that leaves us with an object that has its invariants
|
||||
// intact is necessary in order to handle self-move. This is required by
|
||||
// types that are used with certain operations of the standard library, such
|
||||
// as the default definition of std::swap when both operands target the same
|
||||
// object.
|
||||
Clear();
|
||||
|
||||
// Perform the actual move/destroy operation on the target function.
|
||||
other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_);
|
||||
manager_ = other.manager_;
|
||||
invoker_ = other.invoker_;
|
||||
other.manager_ = EmptyManager;
|
||||
other.invoker_ = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~CoreImpl() { manager_(FunctionToCall::dispose, &state_, &state_); }
|
||||
|
||||
// Check whether or not the AnyInvocable is in the empty state.
|
||||
bool HasValue() const { return invoker_ != nullptr; }
|
||||
|
||||
// Effects: Puts the object into its empty state.
|
||||
void Clear() {
|
||||
manager_(FunctionToCall::dispose, &state_, &state_);
|
||||
manager_ = EmptyManager;
|
||||
invoker_ = nullptr;
|
||||
}
|
||||
|
||||
// Use local (inline) storage for applicable target object types.
|
||||
template <class QualTRef, class... Args>
|
||||
void InitializeStorage(Args&&... args) {
|
||||
if constexpr (IsStoredLocally<RemoveCVRef<QualTRef>>()) {
|
||||
using RawT = RemoveCVRef<QualTRef>;
|
||||
::new (static_cast<void*>(&state_.storage))
|
||||
RawT(std::forward<Args>(args)...);
|
||||
|
||||
invoker_ = LocalInvoker<SigIsNoexcept, ReturnType, QualTRef, P...>;
|
||||
// We can simplify our manager if we know the type is trivially copyable.
|
||||
InitializeLocalManager<RawT>();
|
||||
} else {
|
||||
InitializeRemoteManager<RemoveCVRef<QualTRef>>(
|
||||
std::forward<Args>(args)...);
|
||||
// This is set after everything else in case an exception is thrown in an
|
||||
// earlier step of the initialization.
|
||||
invoker_ = RemoteInvoker<SigIsNoexcept, ReturnType, QualTRef, P...>;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T,
|
||||
typename = absl::enable_if_t<std::is_trivially_copyable<T>::value>>
|
||||
void InitializeLocalManager() {
|
||||
manager_ = LocalManagerTrivial;
|
||||
}
|
||||
|
||||
template <class T,
|
||||
absl::enable_if_t<!std::is_trivially_copyable<T>::value, int> = 0>
|
||||
void InitializeLocalManager() {
|
||||
manager_ = LocalManagerNontrivial<T>;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using HasTrivialRemoteStorage =
|
||||
std::integral_constant<bool, std::is_trivially_destructible<T>::value &&
|
||||
alignof(T) <=
|
||||
ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT>;
|
||||
|
||||
template <class T, class... Args,
|
||||
typename = absl::enable_if_t<HasTrivialRemoteStorage<T>::value>>
|
||||
void InitializeRemoteManager(Args&&... args) {
|
||||
// unique_ptr is used for exception-safety in case construction throws.
|
||||
std::unique_ptr<void, TrivialDeleter> uninitialized_target(
|
||||
::operator new(sizeof(T)), TrivialDeleter(sizeof(T)));
|
||||
::new (uninitialized_target.get()) T(std::forward<Args>(args)...);
|
||||
state_.remote.target = uninitialized_target.release();
|
||||
state_.remote.size = sizeof(T);
|
||||
manager_ = RemoteManagerTrivial;
|
||||
}
|
||||
|
||||
template <class T, class... Args,
|
||||
absl::enable_if_t<!HasTrivialRemoteStorage<T>::value, int> = 0>
|
||||
void InitializeRemoteManager(Args&&... args) {
|
||||
state_.remote.target = ::new T(std::forward<Args>(args)...);
|
||||
manager_ = RemoteManagerNontrivial<T>;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Type trait to determine if the template argument is an AnyInvocable whose
|
||||
// function type is compatible enough with ours such that we can
|
||||
// "move the guts" out of it when moving, rather than having to place a new
|
||||
// object into remote storage.
|
||||
|
||||
template <typename Other>
|
||||
struct IsCompatibleAnyInvocable {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
template <typename Sig>
|
||||
struct IsCompatibleAnyInvocable<AnyInvocable<Sig>> {
|
||||
static constexpr bool value =
|
||||
(IsCompatibleConversion)(static_cast<
|
||||
typename AnyInvocable<Sig>::CoreImpl*>(
|
||||
nullptr),
|
||||
static_cast<CoreImpl*>(nullptr));
|
||||
};
|
||||
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TypeErasedState state_;
|
||||
ManagerType* manager_;
|
||||
InvokerType<SigIsNoexcept, ReturnType, P...>* invoker_;
|
||||
};
|
||||
|
||||
// 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 {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct UnwrapStdReferenceWrapperImpl<std::reference_wrapper<T>> {
|
||||
using type = T&;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using UnwrapStdReferenceWrapper =
|
||||
typename UnwrapStdReferenceWrapperImpl<T>::type;
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// An alias that always yields std::true_type (used with constraints) where
|
||||
// substitution failures happen when forming the template arguments.
|
||||
template <class... T>
|
||||
using TrueAlias =
|
||||
std::integral_constant<bool, sizeof(absl::void_t<T...>*) != 0>;
|
||||
|
||||
/*SFINAE constraints for the conversion-constructor.*/
|
||||
template <class Sig, class F,
|
||||
class = absl::enable_if_t<
|
||||
!std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>>
|
||||
using CanConvert = TrueAlias<
|
||||
absl::enable_if_t<!IsInPlaceType<RemoveCVRef<F>>::value>,
|
||||
absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>,
|
||||
absl::enable_if_t<
|
||||
Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>,
|
||||
absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>;
|
||||
|
||||
/*SFINAE constraints for the std::in_place constructors.*/
|
||||
template <class Sig, class F, class... Args>
|
||||
using CanEmplace = TrueAlias<
|
||||
absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>,
|
||||
absl::enable_if_t<
|
||||
Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>,
|
||||
absl::enable_if_t<std::is_constructible<absl::decay_t<F>, Args...>::value>>;
|
||||
|
||||
/*SFINAE constraints for the conversion-assign operator.*/
|
||||
template <class Sig, class F,
|
||||
class = absl::enable_if_t<
|
||||
!std::is_same<RemoveCVRef<F>, AnyInvocable<Sig>>::value>>
|
||||
using CanAssign = TrueAlias<
|
||||
absl::enable_if_t<Impl<Sig>::template CallIsValid<F>::value>,
|
||||
absl::enable_if_t<
|
||||
Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<F>::value>,
|
||||
absl::enable_if_t<std::is_constructible<absl::decay_t<F>, F>::value>>;
|
||||
|
||||
/*SFINAE constraints for the reference-wrapper conversion-assign operator.*/
|
||||
template <class Sig, class F>
|
||||
using CanAssignReferenceWrapper = TrueAlias<
|
||||
absl::enable_if_t<
|
||||
Impl<Sig>::template CallIsValid<std::reference_wrapper<F>>::value>,
|
||||
absl::enable_if_t<Impl<Sig>::template CallIsNoexceptIfSigIsNoexcept<
|
||||
std::reference_wrapper<F>>::value>>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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(inv_quals, noex) \
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_##noex(inv_quals)
|
||||
|
||||
// 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(inv_quals) \
|
||||
absl::enable_if_t<absl::disjunction< \
|
||||
std::is_nothrow_invocable_r< \
|
||||
ReturnType, UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \
|
||||
P...>, \
|
||||
std::conjunction< \
|
||||
std::is_nothrow_invocable< \
|
||||
UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, P...>, \
|
||||
std::is_same< \
|
||||
ReturnType, \
|
||||
absl::base_internal::invoke_result_t< \
|
||||
UnwrapStdReferenceWrapper<absl::decay_t<F>> inv_quals, \
|
||||
P...>>>>::value>
|
||||
|
||||
#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false(inv_quals)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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_(cv, ref, inv_quals, noex) \
|
||||
template <class ReturnType, class... P> \
|
||||
class Impl<ReturnType(P...) cv ref ABSL_INTERNAL_NOEXCEPT_SPEC(noex)> \
|
||||
: public CoreImpl<noex, ReturnType, P...> { \
|
||||
public: \
|
||||
/*The base class, which contains the datamembers and core operations*/ \
|
||||
using Core = CoreImpl<noex, ReturnType, P...>; \
|
||||
\
|
||||
/*SFINAE constraint to check if F is invocable with the proper signature*/ \
|
||||
template <class F> \
|
||||
using CallIsValid = TrueAlias<absl::enable_if_t<absl::disjunction< \
|
||||
absl::base_internal::is_invocable_r<ReturnType, \
|
||||
absl::decay_t<F> inv_quals, P...>, \
|
||||
std::is_same<ReturnType, \
|
||||
absl::base_internal::invoke_result_t< \
|
||||
absl::decay_t<F> inv_quals, P...>>>::value>>; \
|
||||
\
|
||||
/*SFINAE constraint to check if F is nothrow-invocable when necessary*/ \
|
||||
template <class F> \
|
||||
using CallIsNoexceptIfSigIsNoexcept = \
|
||||
TrueAlias<ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT(inv_quals, \
|
||||
noex)>; \
|
||||
\
|
||||
/*Put the AnyInvocable into an empty state.*/ \
|
||||
Impl() = default; \
|
||||
\
|
||||
/*The implementation of a conversion-constructor from "f*/ \
|
||||
/*This forwards to Core, attaching inv_quals so that the base class*/ \
|
||||
/*knows how to properly type-erase the invocation.*/ \
|
||||
template <class F> \
|
||||
explicit Impl(ConversionConstruct, F&& f) \
|
||||
: Core(TypedConversionConstruct< \
|
||||
typename std::decay<F>::type inv_quals>(), \
|
||||
std::forward<F>(f)) {} \
|
||||
\
|
||||
/*Forward along the in-place construction parameters.*/ \
|
||||
template <class T, class... Args> \
|
||||
explicit Impl(absl::in_place_type_t<T>, Args&&... args) \
|
||||
: Core(absl::in_place_type<absl::decay_t<T> inv_quals>, \
|
||||
std::forward<Args>(args)...) {} \
|
||||
\
|
||||
/*Raises a fatal error when the AnyInvocable is invoked after a move*/ \
|
||||
static ReturnType InvokedAfterMove( \
|
||||
TypeErasedState*, \
|
||||
ForwardedParameterType<P>...) noexcept(noex) { \
|
||||
ABSL_HARDENING_ASSERT(false && "AnyInvocable use-after-move"); \
|
||||
std::terminate(); \
|
||||
} \
|
||||
\
|
||||
InvokerType<noex, ReturnType, P...>* ExtractInvoker() cv { \
|
||||
using QualifiedTestType = int cv ref; \
|
||||
auto* invoker = this->invoker_; \
|
||||
if (!std::is_const<QualifiedTestType>::value && \
|
||||
std::is_rvalue_reference<QualifiedTestType>::value) { \
|
||||
ABSL_ASSERT([this]() { \
|
||||
/* We checked that this isn't const above, so const_cast is safe */ \
|
||||
const_cast<Impl*>(this)->invoker_ = InvokedAfterMove; \
|
||||
return this->HasValue(); \
|
||||
}()); \
|
||||
} \
|
||||
return invoker; \
|
||||
} \
|
||||
\
|
||||
/*The actual invocation operation with the proper signature*/ \
|
||||
ReturnType operator()(P... args) cv ref noexcept(noex) { \
|
||||
assert(this->invoker_ != nullptr); \
|
||||
return this->ExtractInvoker()( \
|
||||
const_cast<TypeErasedState*>(&this->state_), \
|
||||
static_cast<ForwardedParameterType<P>>(args)...); \
|
||||
} \
|
||||
}
|
||||
|
||||
// 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(cv, ref, inv_quals) \
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false); \
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, true)
|
||||
#else
|
||||
#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false)
|
||||
#endif
|
||||
|
||||
// Non-ref-qualified partial specializations
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, , &);
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, , const&);
|
||||
|
||||
// Lvalue-ref-qualified partial specializations
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &, &);
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &, const&);
|
||||
|
||||
// Rvalue-ref-qualified partial specializations
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &&, &&);
|
||||
ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &&, const&&);
|
||||
|
||||
// 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue