From 5d8092adcc119fbb67e4fc5dc2f57b0987d74f64 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Sun, 6 Sep 2026 21:48:58 -0400 Subject: [PATCH] Convert out-of-line metadata into inline metadata storage compact_facade_meta_traits holds metadata inline when it fits in a pointer and out of line otherwise, so two facades can differ in the storage their metadata lands in. Both storages converted only from a storage of their own kind, so a conversion from an out-of-line source to an inline destination did not exist. Give inplace_meta_storage a converting assignment from static_meta_storage that copies the converted metadata into itself, and define static_meta_storage first so that it can be named there. The reverse direction stays absent: static_meta_storage holds a pointer to the static metadata of a facade, and a metadata reached from an inline storage lives inside a proxy rather than in static storage. Remove the converting constructors of both storages and the copy members of inplace_meta_storage. No caller constructs one storage from another, and the copy members only restated what is implicitly declared, since a constructor template is never a copy constructor and an assignment template is never a copy assignment operator. Cover a substitution whose two facades land in different storages. On this branch the substitution translates the metadata through an indirect call and does not reach the new conversion, so the test passes either way. It is the case that starts exercising the conversion once a proxy carries metadata across facades directly. --- include/proxy/v4/detail/facade_meta_traits.h | 53 +++++++++----------- tests/proxy_lifetime_tests.cpp | 26 ++++++++++ 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/include/proxy/v4/detail/facade_meta_traits.h b/include/proxy/v4/detail/facade_meta_traits.h index 8a6298e3..86e7d6d9 100644 --- a/include/proxy/v4/detail/facade_meta_traits.h +++ b/include/proxy/v4/detail/facade_meta_traits.h @@ -140,44 +140,18 @@ struct invoker; PRO4D_DEF_OVERLOAD_SPECIALIZATIONS(PRO4D_DEF_INVOKER) #undef PRO4D_DEF_INVOKER -template -struct PRO4D_ENFORCE_EBO inplace_meta_storage : M { - using M::M; - - inplace_meta_storage() = default; - inplace_meta_storage(const inplace_meta_storage&) = default; - template - requires(std::is_nothrow_convertible_v) - inplace_meta_storage(const inplace_meta_storage& rhs) noexcept - : M(static_cast(*rhs)) {} - inplace_meta_storage& operator=(const inplace_meta_storage&) = default; - template - requires(std::is_nothrow_convertible_v) - inplace_meta_storage& - operator=(const inplace_meta_storage& rhs) noexcept { - static_cast(*this) = static_cast(*rhs); - return *this; - } - - const M& operator*() const noexcept { return *this; } -}; - template struct static_meta_storage { static_meta_storage() = default; - template - requires(std::is_nothrow_convertible_v) - static_meta_storage(const static_meta_storage& rhs) noexcept - : ptr_(std::addressof(static_cast(*rhs))) {} + template + explicit static_meta_storage(std::in_place_type_t

) + : ptr_(std::addressof(storage

)) {} template requires(std::is_nothrow_convertible_v) static_meta_storage& operator=(const static_meta_storage& rhs) noexcept { ptr_ = std::addressof(static_cast(*rhs)); return *this; } - template - explicit static_meta_storage(std::in_place_type_t

) - : ptr_(std::addressof(storage

)) {} bool has_value() const noexcept { return ptr_ != nullptr; } void reset() noexcept { ptr_ = nullptr; } const M& operator*() const noexcept { return *ptr_; } @@ -189,6 +163,27 @@ struct static_meta_storage { static inline const M storage{std::in_place_type

}; }; +template +struct inplace_meta_storage : M { + using M::M; + + template + requires(std::is_nothrow_convertible_v) + inplace_meta_storage& + operator=(const inplace_meta_storage& rhs) noexcept { + static_cast(*this) = *rhs; + return *this; + } + template + requires(std::is_nothrow_convertible_v) + inplace_meta_storage& operator=(const static_meta_storage& rhs) noexcept { + static_cast(*this) = *rhs; + return *this; + } + + const M& operator*() const noexcept { return *this; } +}; + } // namespace detail struct compact_facade_meta_traits { diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 701e2ce4..ed17f4c7 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -1217,6 +1217,32 @@ TEST(ProxyLifetimeTests, Test_CopySubstitution_FromNull) { ASSERT_FALSE(p2.has_value()); } +TEST(ProxyLifetimeTests, Test_CopySubstitution_MixedMetaStorage) { + struct Super : pro::facade_builder // + ::add_convention // + ::support_copy // + ::support_relocation // + ::support_destruction // + ::build {}; + struct Derived : pro::facade_builder // + ::add_facade_with_substitution // + ::build {}; + static_assert( + pro::detail::specialization_of>, + pro::detail::inplace_meta_storage>); + static_assert( + pro::detail::specialization_of>, + pro::detail::static_meta_storage>); + int v = 123; + pro::proxy p1 = &v; + pro::proxy p2 = p1; + ASSERT_EQ(ToString(*p1), "123"); + ASSERT_EQ(ToString(*p2), "123"); +} + TEST(ProxyLifetimeTests, Test_MoveSubstitution_FromValue) { utils::LifetimeTracker tracker; std::vector expected_ops;