blob: 2fa0d1ef4f8363c59abc6ab2df50c4c9613dfc55 [file]
// Copyright 2022 Google LLC
//
// 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
//
// http://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.
#ifndef RIEGELI_BASE_ANY_H_
#define RIEGELI_BASE_ANY_H_
#include <stddef.h>
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <memory>
#include <new> // IWYU pragma: keep
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/any_initializer.h"
#include "riegeli/base/any_internal.h"
#include "riegeli/base/arithmetic.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/closing_ptr.h"
#include "riegeli/base/compare.h"
#include "riegeli/base/dependency.h"
#include "riegeli/base/dependency_base.h"
#include "riegeli/base/dependency_manager.h"
#include "riegeli/base/initializer.h"
#include "riegeli/base/memory_estimator.h"
#include "riegeli/base/temporary_storage.h"
#include "riegeli/base/type_id.h"
#include "riegeli/base/type_traits.h"
namespace riegeli {
namespace any_internal {
// Common base class of `Any` and `AnyRef`.
//
// `ABSL_ATTRIBUTE_TRIVIAL_ABI` is effective if `inline_size == 0`.
template <typename Handle, size_t inline_size, size_t inline_align>
class ABSL_ATTRIBUTE_TRIVIAL_ABI AnyBase
: public WithEqual<AnyBase<Handle, inline_size, inline_align>>,
public ConditionallyTrivialAbi<inline_size == 0> {
public:
// Returns a `Handle` to the `Manager`, or a default `Handle` for an empty
// `AnyBase`.
Handle get() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return methods_and_handle_.handle;
}
// If `Handle` supports `operator*`, `AnyBase<Handle>` can be used as a smart
// pointer to the result of `operator*`, for convenience.
template <typename DependentHandle = Handle,
std::enable_if_t<HasDereference<DependentHandle>::value, int> = 0>
decltype(*std::declval<DependentHandle>()) operator*() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
AssertNotNull("Failed precondition of AnyBase::operator*: null handle");
return *get();
}
template <typename DependentHandle = Handle,
std::enable_if_t<HasArrow<DependentHandle>::value, int> = 0>
Handle operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
AssertNotNull("Failed precondition of AnyBase::operator->: null handle");
return get();
}
// `AnyBase<Handle>` can be compared against `nullptr`. If `Handle` supports
// `operator==` with `nullptr`, then delegates the comparison to `Handle`,
// otherwise returns `true` for a non-empty `AnyBase`.
friend bool operator==(const AnyBase& a, std::nullptr_t) {
return a.EqualNullptr();
}
// If `true`, the `AnyBase` owns the dependent object, i.e. closing the host
// object should close the dependent object.
bool IsOwning() const {
return methods_and_handle_.methods->is_owning(repr_.storage);
}
// If `true`, `get()` stays unchanged when an `AnyBase` is moved.
static constexpr bool kIsStable = inline_size == 0;
// If the stored `Manager` has exactly this type or a reference to it, returns
// a pointer to the `Manager`. Otherwise returns `nullptr`.
template <
typename Manager,
std::enable_if_t<SupportsDependency<Handle, Manager&&>::value, int> = 0>
Manager* GetIf() ABSL_ATTRIBUTE_LIFETIME_BOUND;
template <
typename Manager,
std::enable_if_t<SupportsDependency<Handle, Manager&&>::value, int> = 0>
const Manager* GetIf() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Returns the `TypeId` corresponding to the stored `Manager` type, stripping
// any toplevel reference.
TypeId type_id() const { return methods_and_handle_.methods->type_id; }
// Returns `true` when the stored `Manager` has exactly this type or a
// reference to it.
//
// Same as `type_id() == TypeId::For<Manager>()`.
//
// Same as `GetIf<Manager>() != nullptr` but more efficient if the type
// matches.
template <
typename Manager,
std::enable_if_t<SupportsDependency<Handle, Manager&&>::value, int> = 0>
bool Holds() const {
return type_id() == TypeId::For<Manager>();
}
// Supports `MemoryEstimator`.
friend void RiegeliRegisterSubobjects(const AnyBase* self,
MemoryEstimator& memory_estimator) {
self->methods_and_handle_.methods->register_subobjects(self->repr_.storage,
memory_estimator);
}
protected:
// The state is left uninitialized.
AnyBase() noexcept {}
AnyBase(AnyBase&& that) noexcept;
AnyBase& operator=(AnyBase&& that) noexcept;
~AnyBase() { Destroy(); }
void Reset(std::nullptr_t = nullptr);
// Initializes the state.
//
// If `Manager` is already a compatible `Any` or `AnyRef`, possibly wrapped in
// `ClosingPtrType`, or an rvalue reference to it, adopts its storage instead
// of keeping an indirection. This causes `GetIf()` to see through it.
void Initialize();
template <
typename Manager,
std::enable_if_t<
std::conjunction_v<std::negation<IsAny<Handle, Manager>>,
std::negation<IsAnyClosingPtr<Handle, Manager>>>,
int> = 0>
void Initialize(Manager&& manager);
template <typename Manager,
std::enable_if_t<IsAny<Handle, Manager>::value, int> = 0>
void Initialize(Manager&& manager);
template <typename Manager,
std::enable_if_t<IsAnyClosingPtr<Handle, Manager>::value, int> = 0>
void Initialize(Manager&& manager);
template <
typename Manager,
std::enable_if_t<
std::conjunction_v<std::negation<IsAny<Handle, Manager>>,
std::negation<IsAnyClosingPtr<Handle, Manager>>>,
int> = 0>
void Initialize(Initializer<Manager> manager);
template <typename Manager,
std::enable_if_t<IsAny<Handle, Manager>::value, int> = 0>
void Initialize(Initializer<Manager> manager);
template <typename Manager,
std::enable_if_t<IsAnyClosingPtr<Handle, Manager>::value, int> = 0>
void Initialize(Initializer<Manager> manager);
void InitializeFromAnyInitializer(AnyInitializer<Handle> manager);
template <typename Manager,
std::enable_if_t<!std::is_reference_v<Manager>, int> = 0>
void Adopt(Manager&& manager);
template <typename Manager,
std::enable_if_t<std::is_rvalue_reference_v<Manager>, int> = 0>
void Adopt(Manager&& manager);
// Destroys the state, leaving it uninitialized.
void Destroy();
private:
// For adopting the state from an instantiation with a different `inline_size`
// and `inline_align`.
template <typename OtherHandle, size_t other_inline_size,
size_t other_inline_align>
friend class AnyBase;
// For adopting the state from an instantiation held in an `AnyInitializer`.
friend class AnyInitializer<Handle>;
using Repr = any_internal::Repr<Handle, inline_size, inline_align>;
using MethodsAndHandle = any_internal::MethodsAndHandle<Handle>;
using NullMethods = any_internal::NullMethods<Handle>;
template <typename Manager>
using MethodsFor = any_internal::MethodsFor<
Handle, Manager, IsInline<Handle, Manager, inline_size, inline_align>()>;
static constexpr size_t kAvailableSize =
AvailableSize<Handle, inline_size, inline_align>();
static constexpr size_t kAvailableAlign =
AvailableAlign<Handle, inline_size, inline_align>();
template <typename DependentHandle = Handle,
std::enable_if_t<IsComparableAgainstNullptr<DependentHandle>::value,
int> = 0>
void AssertNotNull(absl::string_view message) const {
RIEGELI_ASSERT(get() != nullptr) << message;
}
template <typename DependentHandle = Handle,
std::enable_if_t<
!IsComparableAgainstNullptr<DependentHandle>::value, int> = 0>
void AssertNotNull(ABSL_ATTRIBUTE_UNUSED absl::string_view message) const {}
template <typename DependentHandle = Handle,
std::enable_if_t<IsComparableAgainstNullptr<DependentHandle>::value,
int> = 0>
bool EqualNullptr() const {
return get() == nullptr;
}
template <typename DependentHandle = Handle,
std::enable_if_t<
!IsComparableAgainstNullptr<DependentHandle>::value, int> = 0>
bool EqualNullptr() const {
return methods_and_handle_.methods == &NullMethods::kMethods;
}
MethodsAndHandle methods_and_handle_;
Repr repr_;
};
} // namespace any_internal
// `Any<Handle>` refers to an optionally owned object which is accessed as
// `Handle` and stored as some `Manager` type decided when the `Any` is
// initialized.
//
// Often `Handle` is some pointer `Base*`, and then `Manager` can be e.g.
// `T*` (not owned), `T` (owned), or `std::unique_ptr<T>` (owned), with some `T`
// derived from `Base`.
//
// `Any<Handle>` holds a `Dependency<Handle, Manager>` for some `Manager` type,
// erasing the `Manager` parameter from the type of the `Any`, or is empty.
template <typename Handle, size_t inline_size = 0, size_t inline_align = 0>
class ABSL_NULLABILITY_COMPATIBLE Any
: public any_internal::AnyBase<Handle, inline_size, inline_align> {
private:
// Indirection through `InliningImpl` is needed for MSVC for some reason.
template <typename... InlineManagers>
struct InliningImpl {
using type =
Any<Handle,
UnsignedMax(inline_size,
sizeof(Dependency<Handle, InlineManagers>)...),
UnsignedMax(inline_align,
alignof(Dependency<Handle, InlineManagers>)...)>;
};
public:
// `Any<Handle>::Inlining<InlineManagers...>` enlarges inline storage of
// `Any<Handle>`.
//
// `InlineManagers` specify the size of inline storage, which allows to avoid
// heap allocation if `Manager` is among `InlineManagers`, or if
// `Dependency<Handle, Manager>` fits there regarding size and alignment.
// By default inline storage is enough for a pointer.
template <typename... InlineManagers>
using Inlining = typename InliningImpl<InlineManagers...>::type;
// Creates an empty `Any`.
Any() noexcept { this->Initialize(); }
/*implicit*/ Any(std::nullptr_t) { this->Initialize(); }
// Holds a `Dependency<Handle, TargetT<Manager>>`.
//
// If `TargetT<Manager>` is already a compatible `Any` or `AnyRef`, possibly
// wrapped in `ClosingPtrType`, or an rvalue reference to it, adopts its
// storage instead of keeping an indirection. This causes `GetIf()` to see
// through it.
template <typename Manager,
std::enable_if_t<
std::conjunction_v<NotSameRef<Any, TargetT<Manager>>,
TargetSupportsDependency<Handle, Manager>>,
int> = 0>
/*implicit*/ Any(Manager&& manager);
template <typename Manager,
std::enable_if_t<
std::conjunction_v<NotSameRef<Any, TargetT<Manager>>,
TargetSupportsDependency<Handle, Manager>>,
int> = 0>
Any& operator=(Manager&& manager);
// Holds the `Dependency` specified when the `AnyInitializer` was constructed.
//
// `AnyInitializer` is accepted as a template parameter to avoid this
// constructor triggering implicit conversions of other parameter types to
// `AnyInitializer`, which causes template instantiation cycles.
template <typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>,
int> = 0>
/*implicit*/ Any(Manager manager);
template <typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>,
int> = 0>
Any& operator=(Manager manager);
// Assignment operator which materializes `Any` from its `Initializer`
// except from the `Any` itself, which is handled below.
template <typename Manager,
std::enable_if_t<std::conjunction_v<SameRef<Any, TargetT<Manager>>,
NotSameRef<Any, Manager>>,
int> = 0>
Any& operator=(Manager&& manager) {
riegeli::Reset(*this, std::forward<Manager>(manager));
return *this;
}
Any(Any&& that) = default;
Any& operator=(Any&& that) = default;
// Makes `*this` equivalent to a newly constructed `Any`. This avoids
// constructing a temporary `Any` and moving from it.
ABSL_ATTRIBUTE_REINITIALIZES void Reset(std::nullptr_t = nullptr) {
Any::AnyBase::Reset();
}
private:
// For `ABSL_NULLABILITY_COMPATIBLE`.
using pointer = std::conditional_t<std::is_pointer_v<Handle>, Handle, void*>;
};
// Specialization of `DependencyManagerImpl<Any<Handle>>`:
// a dependency with ownership determined at runtime.
template <typename Handle, size_t inline_size, size_t inline_align,
typename ManagerStorage>
class DependencyManagerImpl<Any<Handle, inline_size, inline_align>,
ManagerStorage>
: public DependencyBase<ManagerStorage> {
public:
using DependencyManagerImpl::DependencyBase::DependencyBase;
bool IsOwning() const { return this->manager().IsOwning(); }
static constexpr bool kIsStable =
DependencyManagerImpl::DependencyBase::kIsStable ||
Any<Handle, inline_size, inline_align>::kIsStable;
protected:
DependencyManagerImpl(DependencyManagerImpl&& that) = default;
DependencyManagerImpl& operator=(DependencyManagerImpl&& that) = default;
~DependencyManagerImpl() = default;
Handle ptr() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return this->manager().get();
}
};
// Specialization of
// `DependencyManagerImpl<ClosingPtrType<Any<Handle>>>`:
// a dependency with ownership determined at runtime.
template <typename Handle, size_t inline_size, size_t inline_align,
typename ManagerStorage>
class DependencyManagerImpl<
std::unique_ptr<Any<Handle, inline_size, inline_align>, NullDeleter>,
ManagerStorage>
: public DependencyBase<std::conditional_t<
absl::is_trivially_relocatable<std::unique_ptr<
Any<Handle, inline_size, inline_align>, NullDeleter>>::value,
std::unique_ptr<Any<Handle, inline_size, inline_align>, NullDeleter>,
ManagerStorage>> {
public:
using DependencyManagerImpl::DependencyBase::DependencyBase;
bool IsOwning() const {
return this->manager() != nullptr && this->manager()->IsOwning();
}
static constexpr bool kIsStable = true;
protected:
DependencyManagerImpl(DependencyManagerImpl&& that) = default;
DependencyManagerImpl& operator=(DependencyManagerImpl&& that) = default;
~DependencyManagerImpl() = default;
Handle ptr() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return this->manager()->get();
}
};
// `AnyRef<Handle>` refers to an optionally owned object which is accessed as
// `Handle` and was passed as some `Manager` type decided when the `AnyRef` was
// initialized.
//
// Often `Handle` is some pointer `Base*`, and then `Manager` can be e.g.
// `T&` (not owned), `T&&` (owned), or `std::unique_ptr<T>` (owned), with some
// `T` derived from `Base`.
//
// `AnyRef<Handle>` holds a `Dependency<Handle, Manager&&>` (which collapses to
// `Dependency<Handle, Manager&>` if `Manager` is itself an lvalue reference)
// for some `Manager` type, erasing the `Manager` parameter from the type of the
// `AnyRef`, or is empty.
//
// `AnyRef<Handle>(manager)` does not own `manager`, even if it involves
// temporaries, hence it should be used only as a parameter of a function or
// constructor, so that the temporaries outlive its usage. Instead of storing an
// `AnyRef<Handle>` in a variable or returning it from a function, consider
// `riegeli::OwningMaker<Manager>()`, `MakerTypeFor<Manager, ManagerArgs...>`,
// or `Any<Handle>`.
//
// This allows to pass an unowned dependency by lvalue reference instead of by
// pointer, which allows for a more idiomatic API for passing an object which
// does not need to be valid after the function returns. And this allows to pass
// an owned dependency by rvalue reference instead of by value, which avoids
// moving it.
template <typename Handle>
class ABSL_NULLABILITY_COMPATIBLE AnyRef
: public any_internal::AnyBase<Handle, 0, 0> {
public:
// Creates an empty `AnyRef`.
AnyRef() noexcept { this->Initialize(); }
/*implicit*/ AnyRef(std::nullptr_t) { this->Initialize(); }
// Holds a `Dependency<Handle, TargetRefT<Manager>&&>` when
// `TargetRefT<Manager>` is not a reference.
//
// If `TargetT<Manager>` is already a compatible `Any` or `AnyRef`, possibly
// wrapped in `ClosingPtrType`, points to its storage instead of keeping an
// indirection. This causes `GetIf()` to see through it.
template <typename Manager,
std::enable_if_t<
std::conjunction_v<
NotSameRef<AnyRef, TargetT<Manager>>,
NotSameRef<Any<Handle>, TargetT<Manager>>,
std::negation<std::is_reference<TargetRefT<Manager>>>,
SupportsDependency<Handle, TargetRefT<Manager>&&>>,
int> = 0>
/*implicit*/ AnyRef(Manager&& manager ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<TargetRefT<Manager>>&& storage
ABSL_ATTRIBUTE_LIFETIME_BOUND = {});
// Holds a `DependencyRef<Handle, Manager>` when `TargetRefT<Manager>` is a
// reference.
//
// If `TargetT<Manager>` is an rvalue reference to an already a compatible
// `Any` or `AnyRef`, possibly wrapped in `ClosingPtrType`, points to its
// storage instead of keeping an indirection. This causes `GetIf()` to see
// through it.
//
// This constructor is separate so that it does not need `storage`.
template <typename Manager,
std::enable_if_t<std::conjunction_v<
NotSameRef<AnyRef, TargetT<Manager>>,
NotSameRef<Any<Handle>, TargetT<Manager>>,
std::is_reference<TargetRefT<Manager>>,
TargetRefSupportsDependency<Handle, Manager>>,
int> = 0>
/*implicit*/ AnyRef(Manager&& manager ABSL_ATTRIBUTE_LIFETIME_BOUND);
// Adopts the `Dependency` from `Any<Handle>` with no inline storage.
//
// This constructor is separate so that it does not need temporary storage nor
// `ABSL_ATTRIBUTE_LIFETIME_BOUND`.
template <
typename Manager,
std::enable_if_t<std::is_same_v<TargetT<Manager>, Any<Handle>>, int> = 0>
/*implicit*/ AnyRef(Manager&& manager);
// Holds the `Dependency` specified when the `AnyInitializer` was constructed.
//
// Prefer taking parameters as `AnyRef<Handle>` instead of
// `AnyInitializer<Handle>` if they are ultimately always converted to
// `AnyRef<Handle>`, because this constructor may involve heap allocation.
//
// `AnyInitializer` is accepted as a template parameter to avoid this
// constructor triggering implicit conversions of other parameter types to
// `AnyInitializer`, which causes template instantiation cycles.
template <typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>,
int> = 0>
/*implicit*/ AnyRef(Manager manager);
AnyRef(AnyRef&& that) = default;
AnyRef& operator=(AnyRef&&) = delete;
private:
// For `ABSL_NULLABILITY_COMPATIBLE`.
using pointer = std::conditional_t<std::is_pointer_v<Handle>, Handle, void*>;
};
// Specialization of `DependencyManagerImpl<AnyRef<Handle>>`:
// a dependency with ownership determined at runtime.
template <typename Handle, typename ManagerStorage>
class DependencyManagerImpl<AnyRef<Handle>, ManagerStorage>
: public DependencyBase<ManagerStorage> {
public:
using DependencyManagerImpl::DependencyBase::DependencyBase;
bool IsOwning() const { return this->manager().IsOwning(); }
static constexpr bool kIsStable =
DependencyManagerImpl::DependencyBase::kIsStable ||
AnyRef<Handle>::kIsStable;
protected:
DependencyManagerImpl(DependencyManagerImpl&& that) = default;
DependencyManagerImpl& operator=(DependencyManagerImpl&& that) = default;
~DependencyManagerImpl() = default;
Handle ptr() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return this->manager().get();
}
};
// Specialization of
// `DependencyManagerImpl<ClosingPtrType<AnyRef<Handle>, Deleter>>`:
// a dependency with ownership determined at runtime.
template <typename Handle, typename ManagerStorage>
class DependencyManagerImpl<std::unique_ptr<AnyRef<Handle>, NullDeleter>,
ManagerStorage>
: public DependencyBase<std::conditional_t<
absl::is_trivially_relocatable<
std::unique_ptr<AnyRef<Handle>, NullDeleter>>::value,
std::unique_ptr<AnyRef<Handle>, NullDeleter>, ManagerStorage>> {
public:
using DependencyManagerImpl::DependencyBase::DependencyBase;
bool IsOwning() const {
return this->manager() != nullptr && this->manager()->IsOwning();
}
static constexpr bool kIsStable = true;
protected:
DependencyManagerImpl(DependencyManagerImpl&& that) = default;
DependencyManagerImpl& operator=(DependencyManagerImpl&& that) = default;
~DependencyManagerImpl() = default;
Handle ptr() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return this->manager()->get();
}
};
// Implementation details follow.
namespace any_internal {
template <typename Handle, size_t inline_size, size_t inline_align>
inline AnyBase<Handle, inline_size, inline_align>::AnyBase(
AnyBase&& that) noexcept {
if (inline_size == 0) {
// Replace an indirect call to `move()` with plain assignments.
methods_and_handle_.methods = that.methods_and_handle_.methods;
methods_and_handle_.handle = that.methods_and_handle_.handle;
repr_ = that.repr_;
} else {
that.methods_and_handle_.methods->move(that.repr_.storage, repr_.storage,
&methods_and_handle_);
}
that.methods_and_handle_.methods = &NullMethods::kMethods;
that.methods_and_handle_.handle = SentinelHandle<Handle>();
}
template <typename Handle, size_t inline_size, size_t inline_align>
inline AnyBase<Handle, inline_size, inline_align>&
AnyBase<Handle, inline_size, inline_align>::operator=(AnyBase&& that) noexcept {
if (ABSL_PREDICT_TRUE(&that != this)) {
Destroy();
if (inline_size == 0) {
// Replace an indirect call to `move()` with plain assignments.
methods_and_handle_.methods = that.methods_and_handle_.methods;
methods_and_handle_.handle = that.methods_and_handle_.handle;
repr_ = that.repr_;
} else {
that.methods_and_handle_.methods->move(that.repr_.storage, repr_.storage,
&methods_and_handle_);
}
that.methods_and_handle_.methods = &NullMethods::kMethods;
that.methods_and_handle_.handle = SentinelHandle<Handle>();
}
return *this;
}
template <typename Handle, size_t inline_size, size_t inline_align>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize() {
methods_and_handle_.methods = &NullMethods::kMethods;
new (&methods_and_handle_.handle)
Handle(any_internal::SentinelHandle<Handle>());
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<std::conjunction_v<
std::negation<IsAny<Handle, Manager>>,
std::negation<IsAnyClosingPtr<Handle, Manager>>>,
int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Manager&& manager) {
Initialize<Manager>(Initializer<Manager>(std::forward<Manager>(manager)));
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<IsAny<Handle, Manager>::value, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Manager&& manager) {
using ManagerValue = std::remove_reference_t<Manager>;
// `manager.methods_and_handle_.methods->used_size <=
// ManagerValue::kAvailableSize`, hence if
// `ManagerValue::kAvailableSize <= kAvailableSize` then
// `manager.methods_and_handle_.methods->used_size <= kAvailableSize`.
// No need to check possibly at runtime.
if ((ManagerValue::kAvailableSize <= kAvailableSize ||
manager.methods_and_handle_.methods->used_size <= kAvailableSize) &&
// Same for alignment.
(ManagerValue::kAvailableAlign <= kAvailableAlign ||
manager.methods_and_handle_.methods->used_align <= kAvailableAlign)) {
// Adopt `manager` by moving its representation as is.
if (inline_size == 0 || ManagerValue::kAvailableSize == 0) {
// Replace an indirect call to `move()` with plain assignments and
// a memory copy.
//
// This would be safe if `ManagerValue::kAvailableSize != 0` while
// `that.manager.methods_and_handle_.methods->used_size == 0`, but this is
// handled specially only if the condition can be determined at compile
// time.
std::memcpy(&repr_, &manager.repr_,
UnsignedMin(sizeof(repr_), sizeof(manager.repr_)));
methods_and_handle_.methods = manager.methods_and_handle_.methods;
methods_and_handle_.handle = manager.methods_and_handle_.handle;
} else {
manager.methods_and_handle_.methods->move(
manager.repr_.storage, repr_.storage, &methods_and_handle_);
}
manager.methods_and_handle_.methods = &NullMethods::kMethods;
manager.methods_and_handle_.handle = SentinelHandle<Handle>();
return;
}
// Adopt `manager` by moving its representation to the heap if `Manager` is
// a value, or referring to it if `Manager` is a reference.
Adopt<Manager>(std::forward<Manager>(manager));
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<IsAnyClosingPtr<Handle, Manager>::value, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Manager&& manager) {
if (manager == nullptr) {
Initialize();
return;
}
// Adopt `*manager` by referring to its representation.
manager->methods_and_handle_.methods->make_reference(
manager->repr_.storage, repr_.storage, &methods_and_handle_);
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<std::conjunction_v<
std::negation<IsAny<Handle, Manager>>,
std::negation<IsAnyClosingPtr<Handle, Manager>>>,
int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Initializer<Manager> manager) {
methods_and_handle_.methods = &MethodsFor<Manager>::kMethods;
MethodsFor<Manager>::Construct(repr_.storage, &methods_and_handle_.handle,
std::move(manager));
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<IsAny<Handle, Manager>::value, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Initializer<Manager> manager) {
// Materialize `Manager` to adopt its storage.
Initialize<Manager>(std::move(manager).Reference());
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<IsAnyClosingPtr<Handle, Manager>::value, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Initialize(
Initializer<Manager> manager) {
// Materialize `Manager` to adopt its storage.
Initialize<Manager>(std::move(manager).Construct());
}
template <typename Handle, size_t inline_size, size_t inline_align>
inline void
AnyBase<Handle, inline_size, inline_align>::InitializeFromAnyInitializer(
AnyInitializer<Handle> manager) {
std::move(manager).Construct(repr_.storage, &methods_and_handle_,
kAvailableSize, kAvailableAlign);
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<!std::is_reference_v<Manager>, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Adopt(
Manager&& manager) {
manager.methods_and_handle_.methods->move_to_heap(
manager.repr_.storage, repr_.storage, &methods_and_handle_);
manager.methods_and_handle_.methods = &NullMethods::kMethods;
manager.methods_and_handle_.handle = SentinelHandle<Handle>();
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<std::is_rvalue_reference_v<Manager>, int>>
inline void AnyBase<Handle, inline_size, inline_align>::Adopt(
Manager&& manager) {
manager.methods_and_handle_.methods->make_reference(
manager.repr_.storage, repr_.storage, &methods_and_handle_);
}
template <typename Handle, size_t inline_size, size_t inline_align>
inline void AnyBase<Handle, inline_size, inline_align>::Destroy() {
methods_and_handle_.methods->destroy(repr_.storage);
methods_and_handle_.handle.~Handle();
}
template <typename Handle, size_t inline_size, size_t inline_align>
inline void AnyBase<Handle, inline_size, inline_align>::Reset(std::nullptr_t) {
methods_and_handle_.methods->destroy(repr_.storage);
methods_and_handle_.methods = &NullMethods::kMethods;
methods_and_handle_.handle = SentinelHandle<Handle>();
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<SupportsDependency<Handle, Manager&&>::value, int>>
inline Manager* AnyBase<Handle, inline_size, inline_align>::GetIf()
ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (!Holds<Manager>()) return nullptr;
return &methods_and_handle_.methods->get_raw_manager(repr_.storage)
.template Cast<Manager&>();
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <typename Manager,
std::enable_if_t<SupportsDependency<Handle, Manager&&>::value, int>>
inline const Manager* AnyBase<Handle, inline_size, inline_align>::GetIf() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
if (!Holds<Manager>()) return nullptr;
return &methods_and_handle_.methods->get_raw_manager(repr_.storage)
.template Cast<const Manager&>();
}
} // namespace any_internal
template <typename Handle, size_t inline_size, size_t inline_align>
template <
typename Manager,
std::enable_if_t<
std::conjunction_v<NotSameRef<Any<Handle, inline_size, inline_align>,
TargetT<Manager>>,
TargetSupportsDependency<Handle, Manager>>,
int>>
inline Any<Handle, inline_size, inline_align>::Any(Manager&& manager) {
this->template Initialize<TargetT<Manager>>(std::forward<Manager>(manager));
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <
typename Manager,
std::enable_if_t<
std::conjunction_v<NotSameRef<Any<Handle, inline_size, inline_align>,
TargetT<Manager>>,
TargetSupportsDependency<Handle, Manager>>,
int>>
inline Any<Handle, inline_size, inline_align>&
Any<Handle, inline_size, inline_align>::operator=(Manager&& manager) {
this->Destroy();
this->template Initialize<TargetT<Manager>>(std::forward<Manager>(manager));
return *this;
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <
typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>, int>>
inline Any<Handle, inline_size, inline_align>::Any(Manager manager) {
this->InitializeFromAnyInitializer(std::move(manager));
}
template <typename Handle, size_t inline_size, size_t inline_align>
template <
typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>, int>>
inline Any<Handle, inline_size, inline_align>&
Any<Handle, inline_size, inline_align>::operator=(Manager manager) {
this->Destroy();
this->InitializeFromAnyInitializer(std::move(manager));
return *this;
}
template <typename Handle>
template <
typename Manager,
std::enable_if_t<std::conjunction_v<
NotSameRef<AnyRef<Handle>, TargetT<Manager>>,
NotSameRef<Any<Handle>, TargetT<Manager>>,
std::negation<std::is_reference<TargetRefT<Manager>>>,
SupportsDependency<Handle, TargetRefT<Manager>&&>>,
int>>
inline AnyRef<Handle>::AnyRef(Manager&& manager ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<TargetRefT<Manager>>&& storage
ABSL_ATTRIBUTE_LIFETIME_BOUND) {
this->template Initialize<TargetRefT<Manager>&&>(
Initializer<TargetRefT<Manager>>(std::forward<Manager>(manager))
.Reference(std::move(storage)));
}
template <typename Handle>
template <typename Manager,
std::enable_if_t<
std::conjunction_v<NotSameRef<AnyRef<Handle>, TargetT<Manager>>,
NotSameRef<Any<Handle>, TargetT<Manager>>,
std::is_reference<TargetRefT<Manager>>,
TargetRefSupportsDependency<Handle, Manager>>,
int>>
inline AnyRef<Handle>::AnyRef(Manager&& manager ABSL_ATTRIBUTE_LIFETIME_BOUND) {
this->template Initialize<TargetRefT<Manager>>(
std::forward<Manager>(manager));
}
template <typename Handle>
template <typename Manager,
std::enable_if_t<std::is_same_v<TargetT<Manager>, Any<Handle>>, int>>
inline AnyRef<Handle>::AnyRef(Manager&& manager) {
this->template Initialize<TargetT<Manager>>(std::forward<Manager>(manager));
}
template <typename Handle>
template <
typename Manager,
std::enable_if_t<std::is_same_v<Manager, AnyInitializer<Handle>>, int>>
inline AnyRef<Handle>::AnyRef(Manager manager) {
this->InitializeFromAnyInitializer(std::move(manager));
}
} // namespace riegeli
#endif // RIEGELI_BASE_ANY_H_