From 8e1ff6fa06f578e4eb128f1f03084660ddeb374d Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 3 Sep 2026 21:19:19 -0400 Subject: [PATCH] Remove substitution_dispatch A facade added by add_facade is a super of the built facade, and a proxy of the built facade converts to a proxy of the super by carrying the metadata over directly. substitution_dispatch did the same by way of a convention and an indirect call, so it no longer earns its place. Remove the class, the conventions the builder produced for it, and the transformations observer_facade and weak_facade applied to it. basic_facade_builder::add_facade_with_substitution is kept and redirects to add_facade, so existing code keeps compiling. Converting to a view or weak proxy of a super replaces what the removed transformations provided. The accessors of the view and weak conversion dispatches gain a conversion function template over the target facade, constrained on the target being reachable from the target of the own facade, which holds for exactly the supers. The invoked overload names the own facade, unless the super declares the skill itself, in which case it names the super and the metadata answers without a second conversion. Both yield the same pointer. The overload qualifiers and the target facade transform are deduced from the convention's own overload type, so the accessor cannot drift from view_conversion_overload or weak_conversion_overload. --- docs/spec/.pages | 1 - docs/spec/README.md | 1 - docs/spec/basic_facade_builder/README.md | 2 +- docs/spec/basic_facade_builder/add_facade.md | 5 +- .../add_facade_with_substitution.md | 8 +- docs/spec/proxy_view.md | 2 +- docs/spec/skills_as_view.md | 6 +- docs/spec/skills_as_weak.md | 4 +- docs/spec/substitution_dispatch/.pages | 4 - docs/spec/substitution_dispatch/README.md | 71 -------------- docs/spec/substitution_dispatch/accessor.md | 28 ------ .../substitution_dispatch/operator_call.md | 8 -- docs/spec/weak_proxy.md | 2 +- include/proxy/v4/detail/core.h | 94 +------------------ include/proxy/v4/detail/facade_creation.h | 38 +------- include/proxy/v4/detail/skills.h | 29 +++++- include/proxy/v4/proxy.ixx | 1 - tests/proxy_creation_tests.cpp | 58 +++++++++++- tests/proxy_dispatch_tests.cpp | 22 ----- tests/proxy_lifetime_tests.cpp | 7 +- tests/proxy_view_tests.cpp | 46 ++++++++- 21 files changed, 144 insertions(+), 293 deletions(-) delete mode 100644 docs/spec/substitution_dispatch/.pages delete mode 100644 docs/spec/substitution_dispatch/README.md delete mode 100644 docs/spec/substitution_dispatch/accessor.md delete mode 100644 docs/spec/substitution_dispatch/operator_call.md diff --git a/docs/spec/.pages b/docs/spec/.pages index 88c6a3c6..fb90bf32 100644 --- a/docs/spec/.pages +++ b/docs/spec/.pages @@ -18,7 +18,6 @@ nav: - proxy_indirect_accessor: proxy_indirect_accessor - proxy_view
observer_facade: proxy_view.md - proxy: proxy - - substitution_dispatch: substitution_dispatch - weak_dispatch: weak_dispatch - weak_proxy
weak_facade: weak_proxy.md - Alias Templates: diff --git a/docs/spec/README.md b/docs/spec/README.md index 2d29469b..9b31bb04 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -31,7 +31,6 @@ This document provides the API specifications for the C++ library Proxy (version | [`proxy_indirect_accessor`](proxy_indirect_accessor/README.md) | Provides indirection accessibility for `proxy` | | [`proxy_view`
`observer_facade`](proxy_view.md) | Non-owning `proxy` optimized for raw pointer types | | [`proxy`](proxy/README.md) | Wraps a pointer object matching specified facade | -| [`substitution_dispatch`](substitution_dispatch/README.md) | Dispatch type for `proxy` substitution with accessibility | | [`weak_dispatch`](weak_dispatch/README.md) | Weak dispatch type with a default implementation that throws `not_implemented` | | [`weak_proxy`
`weak_facade`](weak_proxy.md) | `proxy` with weak ownership | diff --git a/docs/spec/basic_facade_builder/README.md b/docs/spec/basic_facade_builder/README.md index eaa3c775..fb45b21e 100644 --- a/docs/spec/basic_facade_builder/README.md +++ b/docs/spec/basic_facade_builder/README.md @@ -41,7 +41,7 @@ using facade_builder = | ------------------------------------------------------------ | ------------------------------------------------------------ | | [`add_convention`
`add_indirect_convention`
`add_direct_convention`](add_convention.md) | Adds a convention to the template parameters | | [`add_facade`](add_facade.md) | Adds a facade to the template parameters | -| [`add_facade_with_substitution`](add_facade_with_substitution.md) | Adds a facade to the template parameters, together with [substitution](../substitution_dispatch/README.md) support | +| [`add_facade_with_substitution`](add_facade_with_substitution.md) | Equivalent to [`add_facade`](add_facade.md) | | [`add_reflection`
`add_indirect_reflection`
`add_direct_reflection`](add_reflection.md) | Adds a reflection to the template parameters | | [`add_skill`](add_skill.md) | Adds a custom skill | | [`restrict_layout`](restrict_layout.md) | Specifies maximum `MaxSize` and `MaxAlign` in the template parameters | diff --git a/docs/spec/basic_facade_builder/add_facade.md b/docs/spec/basic_facade_builder/add_facade.md index 6e3b9622..2a5eb076 100644 --- a/docs/spec/basic_facade_builder/add_facade.md +++ b/docs/spec/basic_facade_builder/add_facade.md @@ -20,11 +20,11 @@ The alias template `add_facade` of `basic_facade_builder` needs no indirect call to translate the metadata. The contained value is still copied or relocated as it would be by a copy or a move of a `proxy` of the built facade, which involves an indirect call unless the corresponding [`constraint_level`](../constraint_level.md) is `trivial`. Two consequences of embedding are worth noting. When a super is reachable through more than one other super (a diamond), its metadata is embedded once per path. When the built facade strengthens a [`constraint_level`](../constraint_level.md) that `F` also declares (for example from `nontrivial` to `nothrow`), both levels are represented. Either case makes the metadata larger than the sum of the distinct conventions, and nesting diamonds compounds the effect. Metadata of that size is held out of line and shared by every `proxy` of the facade, so the cost is in static data rather than in `sizeof(proxy)`. -A [`proxy`](../proxy/README.md) of the built facade converts to a `proxy`, subject to the copyability and relocatability of `F`. +A [`proxy`](../proxy/README.md) of the built facade converts to a `proxy`, subject to the copyability and relocatability of `F`. It also converts to a [`proxy_view`](../proxy_view.md)`` when [`as_view`](../skills_as_view.md) is in effect, and to a [`weak_proxy`](../weak_proxy.md)`` when [`as_weak`](../skills_as_weak.md) is. ## Example @@ -89,5 +89,4 @@ int main() { ## See Also -- [`add_facade_with_substitution`](add_facade_with_substitution.md) - [`build`](build.md) diff --git a/docs/spec/basic_facade_builder/add_facade_with_substitution.md b/docs/spec/basic_facade_builder/add_facade_with_substitution.md index 93f90dc0..320e5518 100644 --- a/docs/spec/basic_facade_builder/add_facade_with_substitution.md +++ b/docs/spec/basic_facade_builder/add_facade_with_substitution.md @@ -7,13 +7,7 @@ template using add_facade_with_substitution = basic_facade_builder; ``` -The alias template `add_facade_with_substitution` of `basic_facade_builder` is equivalent to [`add_facade`](add_facade.md)``, except that it always merges a direct convention of [`substitution_dispatch`](../substitution_dispatch/README.md) into `Cs`. This convention enables substitution from a `proxy` of the built [facade](../facade.md) to a `proxy`. - -## Notes - -`add_facade_with_substitution` was introduced in `4.1.0` as a replacement for the deprecated `add_facade` syntax. - -The substitution convention is helpful when an API requires backward compatibility, at the cost of potentially a slightly larger binary size. When substitution is not required, use [`add_facade`](add_facade.md) to guarantee minimal binary size in code generation. +The alias template `add_facade_with_substitution` of `basic_facade_builder` is equivalent to [`add_facade`](add_facade.md)``. ## Example diff --git a/docs/spec/proxy_view.md b/docs/spec/proxy_view.md index 15aa6563..246bf1c7 100644 --- a/docs/spec/proxy_view.md +++ b/docs/spec/proxy_view.md @@ -22,7 +22,7 @@ using proxy_view = proxy>; | Name | Description | | ---------------------------------- | ------------------------------------------------------------ | | `super_types`
*(since 5.0.0)* | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::super_types`. Specifically, for each super `G` in `typename F::super_types`, `observer_facade` is included. | -| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::convention_types`. Specifically, for each convention `C` in `typename F::convention_types`:
- If `C::is_direct` is `false`, include `C` unchanged.
- Otherwise, if `typename C::dispatch_type` is [`substitution_dispatch`](./substitution_dispatch/README.md), include a transformed convention `C'` whose `is_direct` is `true`, `dispatch_type` is still `substitution_dispatch`, and whose `overload_type` is `typename C::overload_type` with a return type of `proxy` replaced by `proxy_view` and qualifiers replaced by `const noexcept`.
- Otherwise `C` is discarded. Duplicates are removed. | +| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::convention_types`. Specifically, for each convention `C` in `typename F::convention_types`, `C` is included when `C::is_direct` is `false`, or otherwise discarded. | | `reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::reflection_types`. Specifically, for each reflection type `R` in `typename F::reflection_types`, `R` is included when `R::is_direct` is `false`, or otherwise discarded. | ## Member Constants of `observer_facade` diff --git a/docs/spec/skills_as_view.md b/docs/spec/skills_as_view.md index aafe7a3e..ffdf5d96 100644 --- a/docs/spec/skills_as_view.md +++ b/docs/spec/skills_as_view.md @@ -10,12 +10,14 @@ template using as_view = /* see below */; ``` -The alias template `as_view` modifies a specialization of [`basic_facade_builder`](basic_facade_builder/README.md) to allow implicit conversion from [`proxy`](proxy/README.md)`` to [`proxy_view`](proxy_view.md)``, where `F` is a built [facade](facade.md) type. +The alias template `as_view` modifies a specialization of [`basic_facade_builder`](basic_facade_builder/README.md) to allow implicit conversion from [`proxy`](proxy/README.md)`` to [`proxy_view`](proxy_view.md)``, where `F` is a built [facade](facade.md) type and `G` is `F` or a super of `F`, reachable via `typename F::super_types` transitively. *Since 5.0.0*: conversion to a view of a super is allowed. Previously only `proxy_view` was available. -Let `p` be a value of type `proxy`, `ptr` be the contained value of `p` (if any), the conversion from type `proxy&` to type `proxy_view` is equivalent to `return observer-ptr{std::addressof(*ptr)}` if `p` contains a value, or otherwise equivalent to `return nullptr`. `observer-ptr` is an exposition-only type that `*observer-ptr`, `*std::as_const(observer-ptr)`, `*std::move(observer-ptr)` and `*std::move(std::as_const(observer-ptr))` are equivalent to `*ptr`, `*std::as_const(ptr)`, `*std::move(ptr)` and `*std::move(std::as_const(ptr))`, respectively. +Let `p` be a value of type `proxy`, `ptr` be the contained value of `p` (if any), the conversion from type `proxy&` to type `proxy_view` is equivalent to `return observer-ptr{std::addressof(*ptr)}` if `p` contains a value, or otherwise equivalent to `return nullptr`. `observer-ptr` is an exposition-only type that `*observer-ptr`, `*std::as_const(observer-ptr)`, `*std::move(observer-ptr)` and `*std::move(std::as_const(observer-ptr))` are equivalent to `*ptr`, `*std::as_const(ptr)`, `*std::move(ptr)` and `*std::move(std::as_const(ptr))`, respectively. ## Notes +A view of a super exposes the conventions and reflections of that super. It observes the same object as `p` without taking ownership, exactly as `proxy_view` does. + `as_view` is useful when a certain context does not take ownership of a `proxy` object. Similar to [`std::unique_ptr::get`](https://en.cppreference.com/w/cpp/memory/unique_ptr/get), [`std::shared_ptr::get`](https://en.cppreference.com/w/cpp/memory/shared_ptr/get) and the [borrowing mechanism in Rust](https://doc.rust-lang.org/rust-by-example/scope/borrow.html). ## Example diff --git a/docs/spec/skills_as_weak.md b/docs/spec/skills_as_weak.md index f2bce87e..b3e32262 100644 --- a/docs/spec/skills_as_weak.md +++ b/docs/spec/skills_as_weak.md @@ -10,9 +10,9 @@ template using as_weak = /* see below */; ``` -The alias template `as_weak` modifies a specialization of [`basic_facade_builder`](basic_facade_builder/README.md) to allow implicit conversion from [`proxy`](proxy/README.md)`` to [`weak_proxy`](weak_proxy.md)``, where `F` is a built [facade](facade.md) type. +The alias template `as_weak` modifies a specialization of [`basic_facade_builder`](basic_facade_builder/README.md) to allow implicit conversion from [`proxy`](proxy/README.md)`` to [`weak_proxy`](weak_proxy.md)``, where `F` is a built [facade](facade.md) type and `G` is `F` or a super of `F`, reachable via `typename F::super_types` transitively, for which converting `weak_proxy` to `weak_proxy` is not potentially-throwing. *Since 5.0.0*: conversion to a weak proxy of a super is allowed. Previously only `weak_proxy` was available. -Let `p` be a value of type `proxy`, `ptr` be the contained value of `p` (if any), `Ptr` be the type of `ptr`, the conversion from type `const proxy&` to type `weak_proxy` is equivalent to `return typename Ptr::weak_type{p}` if `p` contains a value, or otherwise equivalent to `return nullptr`. +Let `p` be a value of type `proxy`, `ptr` be the contained value of `p` (if any), `Ptr` be the type of `ptr`, the conversion from type `const proxy&` to type `weak_proxy` is equivalent to `return typename Ptr::weak_type{p}` if `p` contains a value, or otherwise equivalent to `return nullptr`. ## Example diff --git a/docs/spec/substitution_dispatch/.pages b/docs/spec/substitution_dispatch/.pages deleted file mode 100644 index b1248442..00000000 --- a/docs/spec/substitution_dispatch/.pages +++ /dev/null @@ -1,4 +0,0 @@ -nav: - - substitution_dispatch: README.md - - accessor: accessor.md - - operator(): operator_call.md diff --git a/docs/spec/substitution_dispatch/README.md b/docs/spec/substitution_dispatch/README.md deleted file mode 100644 index c54ba687..00000000 --- a/docs/spec/substitution_dispatch/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Class `substitution_dispatch` - -> Header: `proxy.h` -> Module: `proxy` -> Namespace: `pro::inline v4` -> Since: 4.0.1 - -```cpp -class substitution_dispatch; -``` - -Class `substitution_dispatch` models a [dispatch](../ProDispatch.md) type for `proxy` substitution. It meets the [*ProAccessible* requirements](../ProAccessible.md) of applicable types. - -## Member Functions - -| Name | Description | -| -------------------------------- | -------------------------------------------- | -| (constructor) [nothrow] | constructs an `substitution_dispatch` object | -| [`operator()`](operator_call.md) | invokes the dispatch | - -## Member Types - -| Name | Description | -| ------------------------- | --------------------------------- | -| [`accessor`](accessor.md) | provides accessibility to `proxy` | - -## Example - -```cpp -#include - -#include - -struct Runnable : pro::facade_builder // - ::add_convention, void()> // - ::build {}; - -struct CopyableRunnable : pro::facade_builder // - ::support_copy // - ::add_facade // - ::add_direct_convention() const&, - pro::proxy() &&> // - ::build {}; - -int main() { - pro::proxy p1 = pro::make_proxy( - [] { std::cout << "Lambda expression invoked\n"; }); - - // Implicit conversion via const reference of pro::proxy - pro::proxy p2 = p1; - std::cout << std::boolalpha << p2.has_value() << "\n"; // Prints "true" - - // Implicit conversion via rvalue reference of pro::proxy - pro::proxy p3 = std::move(p1); - std::cout << p1.has_value() << "\n"; // Prints "false" - (*p3)(); // Prints "Lambda expression invoked" - - // Different from implicit_conversion_dispatch, substitution from a null proxy - // is well-formed - pro::proxy p4 = p1; - std::cout << p4.has_value() << "\n"; // Prints "false" -} -``` - -## See Also - -- [`basic_facade_builder::add_facade`](../basic_facade_builder/add_facade.md) -- [alias template `skills::as_view`](../skills_as_view.md) -- [alias template `skills::as_weak`](../skills_as_weak.md) -- [class `implicit_conversion_dispatch`](../implicit_conversion_dispatch/README.md) diff --git a/docs/spec/substitution_dispatch/accessor.md b/docs/spec/substitution_dispatch/accessor.md deleted file mode 100644 index 05f71b3a..00000000 --- a/docs/spec/substitution_dispatch/accessor.md +++ /dev/null @@ -1,28 +0,0 @@ -# Class template `substitution_dispatch::accessor` - -```cpp -// (1) -template -struct accessor { - accessor() = delete; -}; - -// (2) -template - requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) -struct accessor : accessor... { - using accessor::operator return-type-of...; -}; - -// (3) -template -struct accessor() cv ref noex> { - operator proxy() cv ref noex; -}; -``` - -`(1)` The default implementation of `accessor` is not constructible. - -`(2)` When `sizeof...(Os)` is greater than `1`, and `accessor...` are default-constructible, inherits all `accessor...` types and `using` their `operator return-type-of`. `return-type-of` denotes the *return type* of the overload type `O`. - -`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `proxy() cv ref noex`, provides an implicit `operator proxy()` with the same *cv ref noex* specifiers. `accessor::operator proxy()` is equivalent to `return invoke() cv ref noex>(static_cast

>(*this))` when `static_cast(*this).has_value()` is `true`, or `return nullptr` otherwise. diff --git a/docs/spec/substitution_dispatch/operator_call.md b/docs/spec/substitution_dispatch/operator_call.md deleted file mode 100644 index 4da0dff8..00000000 --- a/docs/spec/substitution_dispatch/operator_call.md +++ /dev/null @@ -1,8 +0,0 @@ -# `substitution_dispatch::operator()` - -```cpp -template -T&& operator()(T&& value) const noexcept; // exposition-only -``` - -Returns `std::forward(value)`. When `T` is not cv- or ref-qualified and [`is_bitwise_trivially_relocatable_v`](../is_bitwise_trivially_relocatable.md) is `true`, conversion from the return value to any `proxy` type shall perform bitwise trivial relocation and does not require that `T` is move-constructible. diff --git a/docs/spec/weak_proxy.md b/docs/spec/weak_proxy.md index 0c71ce39..578df57e 100644 --- a/docs/spec/weak_proxy.md +++ b/docs/spec/weak_proxy.md @@ -26,7 +26,7 @@ using weak_proxy = proxy>; | Name | Description | | ---------------------------------- | ----------- | | `super_types`
*(since 5.0.0)* | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::super_types`. Specifically, for each super `S` in `typename F::super_types`, `weak_facade` is included. | -| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that always contains a direct convention whose dispatch type denotes the member function `lock` and whose overload has signature `proxy() const noexcept`. Calling this overload attempts to obtain a strong `proxy`; it returns an empty `proxy` if the object has expired. For each direct convention `C` in `typename F::convention_types` whose `dispatch_type` is [`substitution_dispatch`](./substitution_dispatch/README.md), a transformed convention `C'` is also included, whose `is_direct` is `true`, `dispatch_type` is still `substitution_dispatch`, and whose `overload_type` is `typename C::overload_type` with a return type of `proxy` replaced by `weak_proxy`, preserving qualifiers. All other conventions from `F` are discarded. | +| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains a single direct convention whose dispatch type denotes the member function `lock` and whose overload has signature `proxy() const noexcept`. Calling this overload attempts to obtain a strong `proxy`; it returns an empty `proxy` if the object has expired. All conventions from `F` are discarded. | | `reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains no types. | ## Member Constants of `weak_facade` diff --git a/include/proxy/v4/detail/core.h b/include/proxy/v4/detail/core.h index 2d725dfe..7ecca2ef 100644 --- a/include/proxy/v4/detail/core.h +++ b/include/proxy/v4/detail/core.h @@ -58,7 +58,6 @@ template class proxy_indirect_accessor; template class PRO4D_ENFORCE_EBO proxy; -struct substitution_dispatch; template struct is_bitwise_trivially_relocatable @@ -257,14 +256,6 @@ struct proxy_helper { static const M& get_meta(const proxy_indirect_accessor& p) noexcept { return get_meta(as_proxy(p)); } - template - static proxy make_relocated(void* src) noexcept { - proxy ret; - std::uninitialized_copy_n(static_cast(src), sizeof(P), - ret.ptr_); - ret.meta_ = decltype(ret.meta_){std::in_place_type

}; - return ret; - } template static void* get_ptr(proxy& p) noexcept { return p.ptr_; @@ -440,27 +431,6 @@ struct erased_context { void* p_; }; -// TODO: remove together with substitution_dispatch. -#define PRO4D_DEF_SUBSTITUTION_CONTEXT(...) \ - template \ - struct erased_context() && __VA_ARGS__> { \ - template \ - friend proxy invoke(erased_context ctx) __VA_ARGS__ { \ - if constexpr (is_bitwise_trivially_relocatable_v

) { \ - return proxy_helper::make_relocated(ctx.p_); \ - } else { \ - auto* self = std::launder(static_cast(ctx.p_)); \ - destroying_guard

guard{self}; \ - return proxy{std::move(*self)}; \ - } \ - } \ - \ - void* p_; \ - } -PRO4D_DEF_SUBSTITUTION_CONTEXT(); -PRO4D_DEF_SUBSTITUTION_CONTEXT(noexcept); -#undef PRO4D_DEF_SUBSTITUTION_CONTEXT template using erased_invoker_t = invoker, O>; @@ -1627,25 +1597,14 @@ class observer_ptr { LR lr_; }; -template -using observer_substitution_overload = - proxy_view::facade_type>() const noexcept; template struct observer_conv_traits : std::type_identity {}; -template - requires(C::is_direct && - std::is_same_v) -struct observer_conv_traits - : std::type_identity>> {}; template requires(!C::is_direct) struct observer_conv_traits : std::type_identity {}; template -using observer_conv_types = merge_tuples_t< - std::tuple<>, - composite_t, typename observer_conv_traits::type...>>; +using observer_conv_types = + composite_t, typename observer_conv_traits::type...>; template using observer_refl_types = composite_t, std::conditional_t...>; @@ -1675,52 +1634,8 @@ using weak_lock_overload = proxy() const noexcept; template using weak_super_types = std::tuple...>; -template -struct weak_substitution_overload_traits; -#define PRO4D_DEF_WEAK_SUBSTITUTION_OVERLOAD_TRAITS(oq, pq, ne, ...) \ - template \ - struct weak_substitution_overload_traits() oq ne> \ - : std::type_identity() oq ne> {}; -PRO4D_DEF_OVERLOAD_SPECIALIZATIONS(PRO4D_DEF_WEAK_SUBSTITUTION_OVERLOAD_TRAITS) -#undef PRO4D_DEF_WEAK_SUBSTITUTION_OVERLOAD_TRAITS -template -struct weak_conv_traits : std::type_identity {}; -template - requires(C::is_direct && - std::is_same_v) -struct weak_conv_traits - : std::type_identity::type>> {}; -template -using weak_conv_types = merge_tuples_t< - std::tuple>>, - composite_t, typename weak_conv_traits::type...>>; - } // namespace detail -struct PRO4D_ENFORCE_EBO substitution_dispatch - : detail::cast_dispatch_base { - template - PRO4D_STATIC_CALL(T&&, T&& self) noexcept { - return std::forward(self); - } - - // This overload is not reachable at runtime, but is necessary to ensure - // substitution_dispatch is SFINAE-friendly. - template - PRO4D_STATIC_CALL(auto, T&&) noexcept - requires(std::is_same_v> && - is_bitwise_trivially_relocatable_v) - { - return detail::converter{ - [](std::in_place_type_t>) noexcept -> proxy - requires(proxiable) - { PRO4D_UNREACHABLE(); }}; - } -}; - template struct observer_facade : detail::facade_impl< @@ -1738,8 +1653,9 @@ struct weak_facade : detail::facade_impl< detail::specialization_t, - detail::specialization_t, + std::tuple>>, std::tuple<>, F::max_size, F::max_align, F::copyability, F::relocatability, F::destructibility> { using strong_type = F; diff --git a/include/proxy/v4/detail/facade_creation.h b/include/proxy/v4/detail/facade_creation.h index e9c5cd78..69ce9259 100644 --- a/include/proxy/v4/detail/facade_creation.h +++ b/include/proxy/v4/detail/facade_creation.h @@ -31,36 +31,6 @@ consteval std::size_t max_align_of(std::size_t value) { using ptr_prototype = void* [2]; -template -using copy_conversion_overload = - proxy() const& noexcept(CL >= constraint_level::nothrow); -template -using move_conversion_overload = - proxy() && noexcept(CL >= constraint_level::nothrow); -template -struct add_substitution_conv - : std::type_identity, - std::conditional_t< - CCL == constraint_level::none, void, - conv_impl>>, - std::conditional_t< - RCL == constraint_level::none, void, - conv_impl>>>>> { -}; -template -struct add_substitution_conv : std::type_identity { -}; -template -using add_substitution_conv_t = - add_substitution_conv::type; - } // namespace detail template ; template - using add_facade_with_substitution = basic_facade_builder< - detail::composite_t, detail::add_substitution_conv_t, Rs, - detail::merge_size(MaxSize, F::max_size), - detail::merge_size(MaxAlign, F::max_align), - detail::merge_constraint(Copyability, F::copyability), - detail::merge_constraint(Relocatability, F::relocatability), - detail::merge_constraint(Destructibility, F::destructibility)>; + using add_facade_with_substitution = add_facade; template requires(detail::is_layout_well_formed(PtrSize, PtrAlign)) diff --git a/include/proxy/v4/detail/skills.h b/include/proxy/v4/detail/skills.h index 0c64e581..a8bbd4f3 100644 --- a/include/proxy/v4/detail/skills.h +++ b/include/proxy/v4/detail/skills.h @@ -42,7 +42,32 @@ struct enabled_t {}; template class TT, class... Ctx> concept enabled_for = std::is_base_of_v, T>; -struct view_conversion_dispatch : cast_dispatch_base { +#define PRO4D_DEF_FAW_CAST_ACCESSOR(oq, pq, ne, ...) \ + template class TargetFacade> \ + struct accessor, D, proxy>() oq ne> { \ + template \ + requires(std::is_convertible_v>, \ + proxy>>) \ + operator proxy>() oq ne { \ + if (!static_cast&>(*this).has_value()) { \ + return nullptr; \ + } \ + return invoke< \ + D, proxy pq, proxy>>, \ + F2, F>>>() oq ne>(static_cast pq>(*this)); \ + } \ + } +struct faw_cast_dispatch_base { + template + struct accessor { + accessor() = delete; + }; + PRO4D_DEF_OVERLOAD_SPECIALIZATIONS(PRO4D_DEF_FAW_CAST_ACCESSOR) +}; +#undef PRO4D_DEF_FAW_CAST_ACCESSOR + +struct view_conversion_dispatch : faw_cast_dispatch_base { template PRO4D_STATIC_CALL(auto, T& value) noexcept requires(requires { @@ -57,7 +82,7 @@ struct view_conversion_dispatch : cast_dispatch_base { template using view_conversion_overload = proxy_view() & noexcept; -struct weak_conversion_dispatch : cast_dispatch_base { +struct weak_conversion_dispatch : faw_cast_dispatch_base { template PRO4D_STATIC_CALL(auto, const P& self) noexcept requires(requires(const typename P::weak_type& w) { diff --git a/include/proxy/v4/proxy.ixx b/include/proxy/v4/proxy.ixx index cdf4cce8..19303be9 100644 --- a/include/proxy/v4/proxy.ixx +++ b/include/proxy/v4/proxy.ixx @@ -40,7 +40,6 @@ using v4::proxy_indirect_accessor; using v4::proxy_invoke; using v4::proxy_reflect; using v4::proxy_view; -using v4::substitution_dispatch; using v4::weak_dispatch; using v4::weak_facade; using v4::weak_proxy; diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 3308f179..579fda88 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -168,11 +168,10 @@ struct TestSharedStringable ::add_direct_reflection // ::build {}; -struct TestWeakSharedStringable - : pro::facade_builder // - ::add_facade_with_substitution // - ::add_skill // - ::build {}; +struct TestWeakSharedStringable : pro::facade_builder // + ::add_facade // + ::add_skill // + ::build {}; static_assert(pro::proxiable); static_assert(!pro::proxiable); @@ -932,6 +931,55 @@ TEST(ProxyCreationTests, ASSERT_TRUE(tracker.GetOperations() == expected_ops); } +TEST(ProxyCreationTests, TestMakeProxyShared_WeakOfSuper) { + struct WeakSuper + : pro::facade_builder // + ::support_copy // + ::add_convention // + ::add_skill // + ::build {}; + struct WeakDerived + : pro::facade_builder // + ::add_facade // + ::add_convention, int(int) const> // + ::build {}; + auto p1 = pro::make_proxy_shared(123); + pro::weak_proxy p2 = p1; + { + auto p3 = p2.lock(); + ASSERT_TRUE(p3.has_value()); + ASSERT_EQ(ToString(*p3), "123"); + } + p1.reset(); + ASSERT_FALSE(p2.lock().has_value()); + pro::proxy p4; + pro::weak_proxy p5 = p4; + ASSERT_FALSE(p5.has_value()); +} + +TEST(ProxyCreationTests, TestMakeProxyShared_WeakOfSuperWithoutSkill) { + struct WeakSuper + : pro::facade_builder // + ::support_copy // + ::add_convention // + ::build {}; + struct WeakDerived + : pro::facade_builder // + ::add_facade // + ::add_skill // + ::add_convention, int(int) const> // + ::build {}; + auto p1 = pro::make_proxy_shared(123); + pro::weak_proxy p2 = p1; + { + auto p3 = p2.lock(); + ASSERT_TRUE(p3.has_value()); + ASSERT_EQ(ToString(*p3), "123"); + } + p1.reset(); + ASSERT_FALSE(p2.lock().has_value()); +} + TEST(ProxyCreationTests, TestMakeProxyShared_SharedCompact_FromValue) { utils::LifetimeTracker tracker; std::vector expected_ops; diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index 679940e3..96c5bb85 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -831,28 +831,6 @@ TEST(ProxyDispatchTests, TestFreeAsMemDispatch) { ASSERT_EQ(p->ToString(), "123"); } -TEST(ProxyDispatchTests, TestSubstitutionDispatch) { -#ifdef PRO4D_HAS_FORMAT - struct Base : pro::facade_builder // - ::add_skill // - ::build {}; - struct TestFacade : pro::facade_builder // - ::add_direct_convention() const&, - pro::proxy() &&> // - ::build {}; - pro::proxy p1 = pro::make_proxy(123); - pro::proxy p2 = p1; - ASSERT_TRUE(p1.has_value()); - ASSERT_EQ(std::format("{}", *p2), "123"); - pro::proxy p3 = std::move(p1); - ASSERT_FALSE(p1.has_value()); - ASSERT_EQ(std::format("{}", *p3), "123"); -#else - GTEST_SKIP() << "std::format not available"; -#endif // PRO4D_HAS_FORMAT -} - TEST(ProxyDispatchTests, TestSuperConversion) { #ifdef PRO4D_HAS_FORMAT struct Base : pro::facade_builder // diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 0e5ea6d3..37ed5f34 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -36,7 +36,7 @@ struct TestThrowingDestructionFacade struct TestRttiFacade : pro::facade_builder // ::add_direct_reflection // - ::add_facade_with_substitution // + ::add_facade // ::build {}; // Additional static asserts for super conversion @@ -1304,8 +1304,9 @@ TEST(ProxyLifetimeTests, Test_CopySubstitution_MixedMetaStorage) { ::support_relocation // ::support_destruction // ::build {}; - struct Derived : pro::facade_builder // - ::add_facade_with_substitution // + struct Derived : pro::facade_builder // + ::add_direct_reflection // + ::add_facade // ::build {}; static_assert( pro::detail::specialization_of // - ::add_skill // + struct TestFacade2 : pro::facade_builder // + ::add_facade // + ::add_skill // ::build {}; pro::proxy p1; pro::proxy_view p2 = p1; @@ -162,7 +162,7 @@ TEST(ProxyViewTests, TestSubstitution_FromValue) { ::build {}; struct TestFacade2 : pro::facade_builder // ::support_copy // - ::add_facade_with_substitution // + ::add_facade // ::add_skill // ::build {}; pro::proxy p1 = pro::make_proxy(123); @@ -215,6 +215,44 @@ TEST(ProxyViewTests, TestFacadeAware_FromSuper) { ASSERT_EQ((*p2) + 1, 124); } +TEST(ProxyViewTests, TestFacadeAware_ToSuperView) { + struct TestFacade1 + : pro::facade_builder // + ::add_convention // + ::add_skill // + ::build {}; + struct TestFacade2 + : pro::facade_builder // + ::add_facade // + ::add_convention, int(int) const> // + ::build {}; + pro::proxy p1 = pro::make_proxy(123); + pro::proxy_view p2 = p1; + ASSERT_EQ(ToString(*p2), "123"); + pro::proxy p3; + pro::proxy_view p4 = p3; + ASSERT_FALSE(p4.has_value()); +} + +TEST(ProxyViewTests, TestFacadeAware_ToSuperViewWithoutSkill) { + struct SuperWithoutAsView + : pro::facade_builder // + ::add_convention // + ::add_convention, int(int) const> // + ::add_convention, int(int) const> // + ::build {}; + struct TestFacade2 + : pro::facade_builder // + ::add_facade // + ::add_skill // + ::add_convention, int(int) const> // + ::build {}; + pro::proxy p1 = pro::make_proxy(123); + pro::proxy_view p2 = p1; + ASSERT_EQ(ToString(*p2), "123"); + ASSERT_EQ((*p2) - 1, 122); +} + TEST(ProxyViewTests, TestFacadeAware) { detail::Point_2 v1{1, 1}; detail::Point_2 v2{1, 0};