diff --git a/benchmarks/proxy_management_benchmark.cpp b/benchmarks/proxy_management_benchmark.cpp index 93a4ba5b..6862efec 100644 --- a/benchmarks/proxy_management_benchmark.cpp +++ b/benchmarks/proxy_management_benchmark.cpp @@ -13,6 +13,19 @@ namespace { +struct SmallObject3; + +} // namespace + +namespace pro::inline v4::details { + +template <> +struct tr_override_traits : applicable_traits {}; + +} // namespace pro::inline v4::details + +namespace { + constexpr int TestManagedObjectCount = 600000; constexpr int TypeSeriesCount = 3; diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md index d62a743d..0a61c4bf 100644 --- a/docs/spec/ProFacade.md +++ b/docs/spec/ProFacade.md @@ -12,6 +12,35 @@ A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*P | `F::relocatability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of `P`. | | `F::destructibility` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of `P`. | +## Notes + +Relocatability is defined as *move-construct an object and then destroy the original instance*. Specifically, the value of `F::relocatability` maps to the following requirements on `P`: + +| Value | Requirement on `P` | +| ------------------------------ | ------------------------------------------------------------ | +| `constraint_level::none` | None | +| `constraint_level::nontrivial` | `std::is_move_constructible_v

&& std::is_destructible_v

` | +| `constraint_level::nothrow` | `std::is_nothrow_move_constructible_v

&& std::is_nothrow_destructible_v

` | +| `constraint_level::trivial` | *trivially relocatable* (see below) | + +C++26 introduces the type trait `std::is_trivially_relocatable_v` ([P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html)). The library evaluates *trivial relocatability* as follows: + +- When `std::is_trivially_relocatable_v

` is available + – If the trait evaluates to `true`, the requirement is met. + – If it evaluates to `false`, the library falls back to the **allow-list** (see below). + +- When the trait is **not** available + – A conservative check is used: `std::is_trivially_move_constructible_v

&& std::is_trivially_destructible_v

` + – If that check is `false`, the allow-list is consulted. + +The allow-list contains types that are known to be safely relocatable even when the compiler cannot confirm it: + + - `std::unique_ptr` when `D` is trivially relocatable + - `std::shared_ptr` + - `std::weak_ptr` + +This strategy avoids false negatives caused by gaps in current compiler implementations of the C++26 feature set while maintaining safety. + ## See Also - [concept `proxiable`](proxiable.md) diff --git a/docs/spec/basic_facade_builder/build.md b/docs/spec/basic_facade_builder/build.md index 5a551830..68d36a1f 100644 --- a/docs/spec/basic_facade_builder/build.md +++ b/docs/spec/basic_facade_builder/build.md @@ -20,7 +20,7 @@ Specifies a [facade](../facade.md) type deduced from the template parameters of | `max_size` [static] [constexpr] | `MaxSize == default-size ? sizeof(void*) * 2u : MaxSize` | | `max_align` [static] [constexpr] | `MaxAlign == default-size ? alignof(void*) : MaxAlign` | | `copyability` [static] [constexpr] | `Copyability == default-cl ? constraint_level::none : Copyability` | -| `relocatability` [static] [constexpr] | `Relocatability == default-cl ? constraint_level::nothrow : Relocatability` | +| `relocatability` [static] [constexpr] | `Relocatability == default-cl ? constraint_level::trivial : Relocatability` | | `destructibility` [static] [constexpr] | `Destructibility == default-cl ? constraint_level::nothrow : Destructibility` | ## Notes diff --git a/docs/spec/basic_facade_builder/support_relocation.md b/docs/spec/basic_facade_builder/support_relocation.md index 714cbaf3..78e9b150 100644 --- a/docs/spec/basic_facade_builder/support_relocation.md +++ b/docs/spec/basic_facade_builder/support_relocation.md @@ -9,7 +9,7 @@ The alias template `support_relocation` of `basic_facade_builder= 202502L +#define PROD_TR_IF_ELIGIBLE trivially_relocatable_if_eligible +#else +#define PROD_TR_IF_ELIGIBLE +#endif // __cpp_trivial_relocatability >= 202502L + namespace pro::inline v4 { namespace details { @@ -138,6 +144,9 @@ using add_qualifier_t = typename add_qualifier_traits::type; template using add_qualifier_ptr_t = std::remove_reference_t>*; +template +struct tr_override_traits : inapplicable_traits {}; + template consteval bool has_copyability(constraint_level level) { switch (level) { @@ -164,8 +173,18 @@ consteval bool has_relocatability(constraint_level level) { return std::is_nothrow_move_constructible_v && std::is_nothrow_destructible_v; case constraint_level::trivial: - return std::is_trivially_move_constructible_v && - std::is_trivially_destructible_v; + if constexpr ( +#if __cpp_lib_trivially_relocatable >= 202502L + std::is_trivially_relocatable_v +#else + std::is_trivially_move_constructible_v && + std::is_trivially_destructible_v +#endif // __cpp_lib_trivially_relocatable >= 202502L + ) { + return true; + } else { + return tr_override_traits::applicable; + } default: return false; } @@ -587,6 +606,20 @@ template using merged_composite_accessor = typename composite_accessor_merge_traits::type; +struct tr_blocker { + tr_blocker() = default; + tr_blocker(const tr_blocker&) noexcept {} + tr_blocker& operator=(const tr_blocker&) noexcept { return *this; } +}; +template +struct conditional_tr_accessor_traits : std::type_identity {}; +template +struct conditional_tr_accessor_traits, false> + : std::type_identity> {}; +template +using conditional_tr_accessor_t = + typename conditional_tr_accessor_traits::type; + template struct ptr_traits : inapplicable_traits {}; template @@ -729,9 +762,10 @@ struct facade_traits using indirect_accessor = merged_composite_accessor; - using direct_accessor = + using direct_accessor = conditional_tr_accessor_t< merged_composite_accessor; + typename facade_traits::refl_direct_accessor>, + F::relocatability == constraint_level::trivial>; template static consteval void diagnose_proxiable() { @@ -925,8 +959,9 @@ const R& proxy_reflect(const proxy& p) noexcept { } template -class proxy : public details::facade_traits::direct_accessor, - public details::inplace_ptr> { +class proxy PROD_TR_IF_ELIGIBLE + : public details::facade_traits::direct_accessor, + public details::inplace_ptr> { friend struct details::proxy_helper; static_assert(details::facade_traits::applicable); @@ -945,7 +980,7 @@ class proxy : public details::facade_traits::direct_accessor, proxy_indirect_accessor>() /* Make GCC happy */ { initialize(rhs); } - proxy(proxy&& rhs) noexcept(F::relocatability == constraint_level::nothrow) + proxy(proxy&& rhs) noexcept(F::relocatability >= constraint_level::nothrow) requires(F::relocatability >= constraint_level::nontrivial && F::copyability != constraint_level::trivial) { @@ -1254,8 +1289,9 @@ class indirect_ptr { }; template -class PRO4D_ENFORCE_EBO allocated_ptr : private alloc_aware, - public indirect_ptr> { +class PRO4D_ENFORCE_EBO allocated_ptr PROD_TR_IF_ELIGIBLE + : private alloc_aware, + public indirect_ptr> { public: template allocated_ptr(const Alloc& alloc, Args&&... args) @@ -1289,7 +1325,8 @@ struct PRO4D_ENFORCE_EBO compact_ptr_storage : alloc_aware, inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class compact_ptr : public indirect_ptr> { +class compact_ptr PROD_TR_IF_ELIGIBLE + : public indirect_ptr> { using Storage = compact_ptr_storage; public: @@ -1326,7 +1363,7 @@ struct PRO4D_ENFORCE_EBO shared_compact_ptr_storage inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class shared_compact_ptr +class shared_compact_ptr PROD_TR_IF_ELIGIBLE : public indirect_ptr> { using Storage = shared_compact_ptr_storage; @@ -1369,7 +1406,7 @@ struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base, template class weak_compact_ptr; template -class strong_compact_ptr +class strong_compact_ptr PROD_TR_IF_ELIGIBLE : public indirect_ptr> { friend class weak_compact_ptr; using Storage = strong_weak_compact_ptr_storage; @@ -1416,7 +1453,7 @@ class strong_compact_ptr : indirect_ptr(ptr) {} }; template -class weak_compact_ptr { +class weak_compact_ptr PROD_TR_IF_ELIGIBLE { public: weak_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { @@ -1452,6 +1489,25 @@ class weak_compact_ptr { strong_weak_compact_ptr_storage* ptr_; }; +template + requires(has_relocatability(constraint_level::trivial)) +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; +template + requires(has_relocatability(constraint_level::trivial)) +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; +template +struct tr_override_traits> : applicable_traits {}; + struct weak_conversion_dispatch; template struct weak_ownership_support_traits_impl : inapplicable_traits {}; @@ -2053,7 +2109,7 @@ struct basic_facade_builder { MaxAlign == details::invalid_size ? alignof(details::ptr_prototype) : MaxAlign, Copyability == details::invalid_cl ? constraint_level::none : Copyability, - Relocatability == details::invalid_cl ? constraint_level::nothrow + Relocatability == details::invalid_cl ? constraint_level::trivial : Relocatability, Destructibility == details::invalid_cl ? constraint_level::nothrow : Destructibility>; @@ -2577,6 +2633,7 @@ struct formatter, CharT> { } // namespace std #endif // __STDC_HOSTED__ && __has_include() +#undef PROD_TR_IF_ELIGIBLE #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE #endif // MSFT_PROXY_V4_PROXY_H_ diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index dd29d99a..7a85c489 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -41,11 +41,25 @@ using MockCopyableSmallPtr = using MockTrivialPtr = MockPtr; using MockFunctionPtr = void (*)(); +} // namespace proxy_traits_tests_details + +namespace pro::inline v4::details { + +template +struct tr_override_traits> + : applicable_traits {}; + +} // namespace pro::inline v4::details + +namespace proxy_traits_tests_details { + struct DefaultFacade : pro::facade_builder::build {}; static_assert( std::is_same_v::facade_type, DefaultFacade>); static_assert(DefaultFacade::copyability == pro::constraint_level::none); -static_assert(DefaultFacade::relocatability == pro::constraint_level::nothrow); +static_assert(DefaultFacade::relocatability == pro::constraint_level::trivial); static_assert(DefaultFacade::destructibility == pro::constraint_level::nothrow); static_assert(DefaultFacade::max_size >= 2 * sizeof(void*)); static_assert(DefaultFacade::max_align >= sizeof(void*)); @@ -75,7 +89,7 @@ static_assert(std::is_nothrow_constructible_v< pro::proxy, std::in_place_type_t, MockFunctionPtr>); static_assert(sizeof(pro::proxy) == - 4 * sizeof(void*)); // VTABLE should be embeded + 3 * sizeof(void*)); // VTABLE should be embeded static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); @@ -115,8 +129,6 @@ static_assert(std::is_nothrow_constructible_v, MockFunctionPtr>); static_assert( std::is_nothrow_assignable_v, MockFunctionPtr>); -static_assert(sizeof(pro::proxy) == - 4 * sizeof(void*)); // VTABLE should be embeded struct CopyableFacade : pro::facade_builder // ::support_copy // @@ -158,7 +170,7 @@ static_assert(std::is_nothrow_constructible_v, static_assert( std::is_nothrow_assignable_v, MockFunctionPtr>); static_assert(sizeof(pro::proxy) == - 3 * sizeof(void*)); // VTABLE should not be embeded + 4 * sizeof(void*)); // VTABLE should be embeded struct CopyableSmallFacade : pro::facade_builder // @@ -191,7 +203,7 @@ static_assert( static_assert( std::is_assignable_v, MockFunctionPtr>); static_assert(sizeof(pro::proxy) == - 2 * sizeof(void*)); // VTABLE should not be embeded + 3 * sizeof(void*)); // VTABLE should be embeded struct TrivialFacade : pro::facade_builder // ::restrict_layout //