Skip to content

Commit

Permalink
Let constructing AnyRef<Handle> from `Any<Handle, inline_size, inli…
Browse files Browse the repository at this point in the history
…ne_align>`

adopt the state if possible instead of forming a reference.

This is done by extending `any_internal::IsAny` to accept also rvalue references
to `Any` and `AnyRef`, so that private `Initialize()` functions recognize also
such rvalue references as types to consider adopting the state from.

This required adapting these functions, as well as
`AnyInitializer::ConstructMethod()` which also uses `any_internal::IsAny`,
to apply `std::remove_reference_t` for accessing static members of the `Any`
or `AnyRef`, because rvalue references are not transparent in this case.

PiperOrigin-RevId: 713281200
  • Loading branch information
QrczakMK committed Jan 8, 2025
1 parent 865a8c5 commit 9a59e6e
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 21 deletions.
25 changes: 10 additions & 15 deletions riegeli/base/any.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class

// Initializes the state, avoiding a redundant indirection and adopting them
// from `manager` instead if `Manager` is already a compatible `Any` or
// `AnyRef`.
// `AnyRef`, or an rvalue reference to it.
void Initialize();
template <typename Manager,
std::enable_if_t<!IsAny<Handle, Manager>::value, int> = 0>
Expand Down Expand Up @@ -617,24 +617,19 @@ 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 <=
// Manager::kAvailableSize`, hence if
// `Manager::kAvailableSize <=
// AvailableSize<Handle, inline_size, inline_align>()` then
// `manager.methods_and_handle_.methods->used_size <=
// AvailableSize<Handle, inline_size, inline_align>()`.
// ManagerValue::kAvailableSize`, hence if
// `ManagerValue::kAvailableSize <= kAvailableSize` then
// `manager.methods_and_handle_.methods->used_size <= kAvailableSize`.
// No need to check possibly at runtime.
if ((Manager::kAvailableSize <=
AvailableSize<Handle, inline_size, inline_align>() ||
manager.methods_and_handle_.methods->used_size <=
AvailableSize<Handle, inline_size, inline_align>()) &&
if ((ManagerValue::kAvailableSize <= kAvailableSize ||
manager.methods_and_handle_.methods->used_size <= kAvailableSize) &&
// Same for alignment.
(Manager::kAvailableAlign <=
AvailableAlign<Handle, inline_size, inline_align>() ||
manager.methods_and_handle_.methods->used_align <=
AvailableAlign<Handle, inline_size, inline_align>())) {
(ManagerValue::kAvailableAlign <= kAvailableAlign ||
manager.methods_and_handle_.methods->used_align <= kAvailableAlign)) {
// Adopt `manager` instead of wrapping it.
if (Manager::kAvailableSize == 0 || inline_size == 0) {
if (inline_size == 0 || ManagerValue::kAvailableSize == 0) {
// Replace an indirect call to `methods_and_handle_.methods->move()` with
// a plain assignment of `methods_and_handle_.handle` and a memory copy of
// `repr_`.
Expand Down
12 changes: 7 additions & 5 deletions riegeli/base/any_initializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,19 +195,21 @@ void AnyInitializer<Handle>::ConstructMethod(
TypeErasedRef context, MethodsAndHandle& methods_and_handle,
Storage storage, size_t available_size, size_t available_align) {
using Target = TargetT<Manager>;
using TargetValue = std::remove_reference_t<Target>;
// Materialize `Target` to consider adopting its storage.
[&](Target&& target) {
// `target.methods_and_handle_.methods->used_size <=
// Target::kAvailableSize`, hence if `Target::kAvailableSize == 0` then
// TargetValue::kAvailableSize`, hence if
// `TargetValue::kAvailableSize == 0` then
// `target.methods_and_handle_.methods->used_size <= available_size`.
// No need to check possibly at runtime.
if ((Target::kAvailableSize == 0 ||
if ((TargetValue::kAvailableSize == 0 ||
target.methods_and_handle_.methods->used_size <= available_size) &&
// Same for alignment.
(Target::kAvailableAlign == 0 ||
(TargetValue::kAvailableAlign == 0 ||
target.methods_and_handle_.methods->used_align <= available_align)) {
// Adopt `target` instead of wrapping it.
if (Target::kAvailableSize == 0) {
if (TargetValue::kAvailableSize == 0) {
// Replace an indirect call to `methods_and_handle_.methods->move()`
// with a plain assignment of `methods_and_handle_.handle` and a memory
// copy of `repr_`.
Expand Down Expand Up @@ -238,7 +240,7 @@ void AnyInitializer<Handle>::ConstructMethod(
// necessary: `Target` is always an `Any`, never an lvalue reference.
MethodsFor<Target, /*is_inline=*/false>::Construct(
storage, &methods_and_handle.handle, std::move(target));
}(Initializer<TargetT<Manager>>(context.Cast<Manager>()).Reference());
}(Initializer<Target>(context.Cast<Manager>()).Reference());
}

} // namespace riegeli
Expand Down
7 changes: 6 additions & 1 deletion riegeli/base/any_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,22 @@ struct MethodsFor;
template <typename Handle>
struct NullMethods;

// `IsAny` detects `Any` or `AnyRef` type with the given `Handle`.
// `IsAny` detects `Any` or `AnyRef` type with the given `Handle`, or an rvalue
// reference to it.

template <typename Handle, typename T>
struct IsAny : std::false_type {};

template <typename Handle, size_t inline_size, size_t inline_align>
struct IsAny<Handle, Any<Handle, inline_size, inline_align>> : std::true_type {
};

template <typename Handle>
struct IsAny<Handle, AnyRef<Handle>> : std::true_type {};

template <typename Handle, typename T>
struct IsAny<Handle, T&&> : IsAny<Handle, T> {};

// Implementation details follow.

template <typename Handle>
Expand Down

0 comments on commit 9a59e6e

Please sign in to comment.