From 0f7ddd321dd74d602210aed4bd9daf85bdf80c1a Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 14 Jul 2025 10:27:18 +0800 Subject: [PATCH 1/3] Improve design quality --- .github/workflows/bvt-msvc.yml | 10 +- docs/faq.md | 7 + docs/spec/.pages | 2 - docs/spec/PRO_DEF_FREE_AS_MEM_DISPATCH.md | 18 +- docs/spec/PRO_DEF_FREE_DISPATCH.md | 18 +- docs/spec/PRO_DEF_MEM_DISPATCH.md | 18 +- docs/spec/ProBasicFacade.md | 8 +- docs/spec/ProDispatch.md | 36 +- docs/spec/ProFacade.md | 7 +- docs/spec/ProReflection.md | 2 +- docs/spec/README.md | 4 +- docs/spec/access_proxy.md | 73 -- docs/spec/allocate_proxy.md | 2 +- docs/spec/basic_facade_builder/README.md | 26 +- .../basic_facade_builder/add_convention.md | 2 +- docs/spec/basic_facade_builder/add_facade.md | 11 +- .../basic_facade_builder/add_reflection.md | 20 +- docs/spec/basic_facade_builder/build.md | 41 +- .../basic_facade_builder/restrict_layout.md | 4 +- docs/spec/basic_facade_builder/support.md | 2 +- .../spec/basic_facade_builder/support_copy.md | 4 +- .../support_destruction.md | 4 +- .../support_relocation.md | 4 +- docs/spec/constraint_level.md | 2 +- .../explicit_conversion_dispatch/accessor.md | 18 +- .../implicit_conversion_dispatch/accessor.md | 18 +- docs/spec/operator_dispatch/README.md | 6 +- docs/spec/operator_dispatch/accessor.md | 62 +- docs/spec/proxiable_ptr_constraints.md | 56 -- docs/spec/proxy/README.md | 2 +- docs/spec/proxy/assignment.md | 34 +- docs/spec/proxy/constructor.md | 18 +- docs/spec/proxy/destructor.md | 10 +- docs/spec/proxy/emplace.md | 8 +- docs/spec/proxy/indirection.md | 2 - docs/spec/proxy/reset.md | 4 +- docs/spec/proxy/swap.md | 8 +- docs/spec/proxy_indirect_accessor.md | 3 +- docs/spec/proxy_invoke.md | 52 +- docs/spec/proxy_reflect.md | 14 +- docs/spec/proxy_view.md | 10 +- docs/spec/skills_rtti/proxy_cast.md | 30 +- docs/spec/skills_rtti/proxy_typeid.md | 10 +- docs/spec/weak_proxy.md | 10 +- include/proxy/v4/proxy.h | 818 ++++++++---------- include/proxy/v4/proxy.ixx | 12 - include/proxy/v4/proxy_fmt.h | 8 +- include/proxy/v4/proxy_macros.h | 248 +++--- .../freestanding/proxy_freestanding_tests.cpp | 5 +- tests/proxy_creation_tests.cpp | 4 +- tests/proxy_dispatch_tests.cpp | 25 +- tests/proxy_fmt_format_tests.cpp | 26 +- tests/proxy_format_tests.cpp | 26 +- tests/proxy_invocation_tests.cpp | 6 +- tests/proxy_reflection_tests.cpp | 4 +- tests/proxy_rtti_tests.cpp | 36 +- tests/proxy_traits_tests.cpp | 119 ++- tests/utils.h | 4 +- 58 files changed, 843 insertions(+), 1198 deletions(-) delete mode 100644 docs/spec/access_proxy.md delete mode 100644 docs/spec/proxiable_ptr_constraints.md diff --git a/.github/workflows/bvt-msvc.yml b/.github/workflows/bvt-msvc.yml index 941ba0c0..bf38ae2f 100644 --- a/.github/workflows/bvt-msvc.yml +++ b/.github/workflows/bvt-msvc.yml @@ -12,11 +12,11 @@ jobs: - name: build and run test with MSVC run: | - cmake -B build -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE - cmake --build build --config Release -j - ctest --test-dir build -j - mkdir build\drop > $null - .\tools\dump_build_env_msvc.ps1 -OutputPath build\drop\env-info.json + cmake -B build -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` + && cmake --build build --config Release -j ` + && ctest --test-dir build -j ` + && mkdir build\drop > $null ` + && .\tools\dump_build_env_msvc.ps1 -OutputPath build\drop\env-info.json - name: run benchmarks run: | diff --git a/docs/faq.md b/docs/faq.md index df4b8add..14fa11ca 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -8,6 +8,7 @@ - [**How to integrate "Proxy" into my project?**](#how-integrate) - [**My existing project uses virtual functions. How should I migrate to "Proxy"?**](#how-migrate) - [**How is the performance compared to virtual functions?**](#performance) +- [**How does "Proxy" compare with CRTP in terms of performance and lifetime management?**](#crtp-performance) - [**Why is "Proxy" based on pointer semantics rather than value semantics like `std::function`?**](#why-pointer) - [**Why does "Proxy" define several macros instead of modern C++ facilities?**](#why-macros) - [**What is the standardization progress of this library?**](#standardization) @@ -51,6 +52,12 @@ Follow the 4 steps below to upgrade an existing project from using virtual funct The design of "Proxy" follows the [zero-overhead principle](https://en.cppreference.com/w/cpp/language/Zero-overhead_principle). With general compiler optimizations, "Proxy" is expected to generate high quality code in most scenarios that is not worse than an equivalent hand-written implementation with or without virtual functions. In practice, "Proxy" usually demonstrates better performance in indirect invocations than virtual functions, and in lifetime management than standard smart pointers or polymorphic wrappers. Please refer to our [blog post](https://devblogs.microsoft.com/cppblog/analyzing-the-performance-of-the-proxy-library/) for more details. +### How does "Proxy" compare with CRTP in terms of performance and lifetime management? + +CRTP (Curiously Recurring Template Pattern) delivers compile-time polymorphism only. With CRTP the compiler sees the exact type of the callee, so it usually emits a direct call that can be inlined and optimized aggressively. `proxy` inserts one level of indirection to obtain runtime polymorphism. In our internal micro-benchmarks a call through CRTP is roughly **1.5x-4x faster** than the same call routed through `proxy`; the exact factor depends on inlining decisions, cache locality, and argument size. If every nanosecond counts and the set of concrete types is known at compile time, CRTP can be the better choice. When you need runtime substitution, heterogeneous containers, or hot-swappable implementations, the slight overhead of `proxy` buys you those features. + +CRTP never erases the concrete type, so each object is still manipulated directly and its lifetime is controlled by normal C++ ownership rules (stack, `new`, smart pointers, etc.). `proxy`, on the other hand, is a type-erasure facility: it stores any object behind an indirect interface and lets you pick a lifetime model (raw pointer, unique/shared ownership, arena, etc.) at the call-site. Because CRTP has no unified runtime representation, comparing "lifetime-management performance" with `proxy` is meaningless. The capability simply does not exist in CRTP. + ### Why is "Proxy" based on pointer semantics rather than value semantics like [std::function](https://en.cppreference.com/w/cpp/utility/functional/function)? At the beginning, we explored the feasibility of designing a general-purpose polymorphic wrapper based on value semantics, just like [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function). It is technically feasible, but not as good as those languages with [GC](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) like C# or Java in terms of usability. We had a hard time refining the theory of [OOP](https://en.wikipedia.org/wiki/Object-oriented_programming) and finally realized that indirection is the nature of computer science, and decided to leverage the concept of pointer in C++ as the basis of "Proxy". diff --git a/docs/spec/.pages b/docs/spec/.pages index db14717a..ccb95e58 100644 --- a/docs/spec/.pages +++ b/docs/spec/.pages @@ -14,7 +14,6 @@ nav: - implicit_conversion_dispatch: implicit_conversion_dispatch - not_implemented: not_implemented.md - operator_dispatch: operator_dispatch - - proxiable_ptr_constraints: proxiable_ptr_constraints.md - proxy_indirect_accessor: proxy_indirect_accessor.md - proxy_view
observer_facade: proxy_view.md - proxy: proxy @@ -27,7 +26,6 @@ nav: - skills::format
skills::wformat: skills_format.md - "skills::rtti
skills::indirect_rtti
skills::direct_rtti": skills_rtti - Functions: - - access_proxy: access_proxy.md - allocate_proxy_shared: allocate_proxy_shared.md - allocate_proxy: allocate_proxy.md - make_proxy_inplace: make_proxy_inplace.md diff --git a/docs/spec/PRO_DEF_FREE_AS_MEM_DISPATCH.md b/docs/spec/PRO_DEF_FREE_AS_MEM_DISPATCH.md index bcd13785..b7eca2e6 100644 --- a/docs/spec/PRO_DEF_FREE_AS_MEM_DISPATCH.md +++ b/docs/spec/PRO_DEF_FREE_AS_MEM_DISPATCH.md @@ -17,7 +17,7 @@ PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name); PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name); ``` -`(1)` Equivalent to `PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, func_name);` +`(1)` Equivalent to `PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, func_name)`. `(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility via member function overloads named `accessibility_func_name`. Effectively equivalent to: @@ -30,19 +30,19 @@ struct dispatch_name { return func_name(std::forward(self), std::forward(args)...); } - template + template struct accessor { accessor() = delete; }; - template - requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) - struct accessor : accessor... { - using accessor::accessibility_func_name ...; + template + requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) + struct accessor : accessor... { + using accessor::accessibility_func_name ...; }; - template - struct accessor { + template + struct accessor { R accessibility_func_name(Args... args) cv ref noex { - return pro::proxy_invoke(pro::access_proxy(std::forward(*this)), std::forward(args)...); + return pro::proxy_invoke(static_cast

>(*this), std::forward(args)...); } }; } diff --git a/docs/spec/PRO_DEF_FREE_DISPATCH.md b/docs/spec/PRO_DEF_FREE_DISPATCH.md index 06602b75..ded5483a 100644 --- a/docs/spec/PRO_DEF_FREE_DISPATCH.md +++ b/docs/spec/PRO_DEF_FREE_DISPATCH.md @@ -16,7 +16,7 @@ PRO_DEF_FREE_DISPATCH(dispatch_name, func_name); PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, accessibility_func_name); ``` -`(1)` Equivalent to `PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, func_name);` +`(1)` Equivalent to `PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, func_name)`. `(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility via free function overloads named `accessibility_func_name`. Effectively equivalent to: @@ -29,17 +29,17 @@ struct dispatch_name { return func_name(std::forward(self), std::forward(args)...); } - template + template struct accessor { accessor() = delete; }; - template - requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) - struct accessor : accessor... {}; - template - struct accessor { - friend R accessibility_func_name(accessor_arg cv ref self, Args... args) noex { - return pro::proxy_invoke(pro::access_proxy(std::forward(self)), std::forward(args)...); + template + requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) + struct accessor : accessor... {}; + template + struct accessor { + friend R accessibility_func_name(P cv self, Args... args) noex { + return pro::proxy_invoke(static_cast

>(self), std::forward(args)...); } }; } diff --git a/docs/spec/PRO_DEF_MEM_DISPATCH.md b/docs/spec/PRO_DEF_MEM_DISPATCH.md index 67634530..2f100e43 100644 --- a/docs/spec/PRO_DEF_MEM_DISPATCH.md +++ b/docs/spec/PRO_DEF_MEM_DISPATCH.md @@ -16,7 +16,7 @@ PRO_DEF_MEM_DISPATCH(dispatch_name, func_name); PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name); ``` -`(1)` Equivalent to `PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, func_name);` +`(1)` Equivalent to `PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, func_name)`. `(2)` Defines a class named `dispatch_name` of member function call expressions of `func_name` with accessibility via member function overloads named `accessibility_func_name`. Effectively equivalent to: @@ -29,19 +29,19 @@ struct dispatch_name { return std::forward(self).func_name(std::forward(args)...); } - template + template struct accessor { accessor() = delete; }; - template - requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) - struct accessor : accessor... { - using accessor::accessibility_func_name ...; + template + requires(sizeof...(Os) > 1u && (std::is_constructible_v> && ...)) + struct accessor : accessor... { + using accessor::accessibility_func_name ...; }; - template - struct accessor { + template + struct accessor { R accessibility_func_name(Args... args) cv ref noex { - return pro::proxy_invoke(pro::access_proxy(std::forward(*this)), std::forward(args)...); + return pro::proxy_invoke(static_cast

>(*this), std::forward(args)...); } }; } diff --git a/docs/spec/ProBasicFacade.md b/docs/spec/ProBasicFacade.md index c23ec74e..85fd877b 100644 --- a/docs/spec/ProBasicFacade.md +++ b/docs/spec/ProBasicFacade.md @@ -5,8 +5,12 @@ A type `F` meets the *ProBasicFacade* requirements if the following expressions | Expressions | Semantics | | ------------------------------ | ------------------------------------------------------------ | | `typename F::convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Cs`. Each type `C` in `Cs` shall meet the [*ProBasicConvention* requirements](ProBasicConvention.md). | -| `typename F::reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Rs`. Each type `R` in `Rs` shall define reflection on pointer types. | -| `F::constraints` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`proxiable_ptr_constraints`](proxiable_ptr_constraints.md) that defines constraints to pointer types. | +| `typename F::reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Rs`. Each type `R` in `Rs` shall define reflection data structure. | +| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum size of a pointer type. | +| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum alignment of a pointer type. | +| `F::copyability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required copyability of a pointer type. | +| `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 a pointer type. | +| `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 a pointer type. | ## See Also diff --git a/docs/spec/ProDispatch.md b/docs/spec/ProDispatch.md index 4774d8e7..c5b0be5e 100644 --- a/docs/spec/ProDispatch.md +++ b/docs/spec/ProDispatch.md @@ -1,27 +1,25 @@ # Named requirements: *ProDispatch* -A type `D` meets the *ProDispatch* requirements of types `T` and `O` if `D` is a [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType), `O` meets the [*ProOverload* requirelemts](ProOverload.md), and the following expressions are well-formed and have the specified semantics (let `R` be return type of `O`, `Args...` be the argument types of `O`. `args...` denotes values of type `Args...`, `v` denotes a value of type `T`, `cv` denotes a value of type `const T`). +A type `D` meets the *ProDispatch* requirements of types `T` and `O` if `O` meets the [*ProOverload* requirelemts](ProOverload.md), and the following expressions are well-formed and have the specified semantics (let `R` be return type of `O`, `Args...` be the argument types of `O`. `args...` denotes values of type `Args...`, `v` denotes a value of type `T`, `cv` denotes a value of type `const T`). + +| Expressions | Semantics | +| ----------- | ----------------------------------------------- | +| `D()` | Creates an object of type `D`, shall not throw. | | Definitions of `O` | Expressions | Semantics | | ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `R(Args...)` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, may throw. | -| `R(Args...) noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, shall not throw. | -| `R(Args...) &` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, may throw. | -| `R(Args...) & noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, shall not throw. | -| `R(Args...) &&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, std::move(v), std::forward(args)...)` | Invokes dispatch type `D` with a rvalue reference of type `T` and `args...`, may throw. | -| `R(Args...) && noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, std::move(v), std::forward(args)...)` | Invokes dispatch type `D` with a rvalue reference of type `T` and `args...`, shall not throw. | -| `R(Args...) const` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, may throw. | -| `R(Args...) const noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, shall not throw. | -| `R(Args...) cosnt&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, cv, std::forward(args)...)`, or
`d(nullptr, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, may throw. | -| `R(Args...) const& noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, shall not throw. | -| `R(Args...) const&&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, std::move(cv), std::forward(args)...)` | Invokes dispatch type `D` with a const rvalue reference of type `T` and `args...`, may throw. | -| `R(Args...) const&& noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, std::move(cv), std::forward(args)...)` | Invokes dispatch type `D` with a const rvalue reference of type `T` and `args...`, shall not throw. | - -Or, - -| Definitions of `O` | Expressions | Semantics | -| -------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `R(Args...)` *cv ref noex* | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D{}, nullptr, std::forward(args)...)` | Invokes the dispatch type `D` with `nullptr` and `args...`, may or may not throw depending on `noex`. | +| `R(Args...)` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, may throw. | +| `R(Args...) noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, shall not throw. | +| `R(Args...) &` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, may throw. | +| `R(Args...) & noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), v, std::forward(args)...)` | Invokes dispatch type `D` with an lvalue reference of type `T` and `args...`, shall not throw. | +| `R(Args...) &&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), std::move(v), std::forward(args)...)` | Invokes dispatch type `D` with a rvalue reference of type `T` and `args...`, may throw. | +| `R(Args...) && noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), std::move(v), std::forward(args)...)` | Invokes dispatch type `D` with a rvalue reference of type `T` and `args...`, shall not throw. | +| `R(Args...) const` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, may throw. | +| `R(Args...) const noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, shall not throw. | +| `R(Args...) cosnt&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, may throw. | +| `R(Args...) const& noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), cv, std::forward(args)...)` | Invokes dispatch type `D` with a const reference of type `T` and `args...`, shall not throw. | +| `R(Args...) const&&` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), std::move(cv), std::forward(args)...)` | Invokes dispatch type `D` with a const rvalue reference of type `T` and `args...`, may throw. | +| `R(Args...) const&& noexcept` | [`INVOKE`](https://en.cppreference.com/w/cpp/utility/functional)`(D(), std::move(cv), std::forward(args)...)` | Invokes dispatch type `D` with a const rvalue reference of type `T` and `args...`, shall not throw. | ## See Also diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md index e087bb71..d62a743d 100644 --- a/docs/spec/ProFacade.md +++ b/docs/spec/ProFacade.md @@ -1,11 +1,16 @@ # Named requirements: *ProFacade* -A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*ProBasicFacade* requirements](ProBasicFacade.md), and `P` meets the requirements defined by [`F::constraints`](proxiable_ptr_constraints.md), and the following expressions are well-formed and have the specified semantics. +A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*ProBasicFacade* requirements](ProBasicFacade.md), and the following expressions are well-formed and have the specified semantics. | Expressions | Semantics | | ------------------------------ | ------------------------------------------------------------ | | `typename F::convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Cs`. Each type `C` in `Cs` shall meet the [*ProConvention* requirements](ProConvention.md) of `P`. | | `typename F::reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Rs`. Each type `R` in `Rs` shall meet the [*ProReflection* requirements](ProReflection.md) of `P`. | +| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that shall be greater than or equal to `sizeof(P)`. | +| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that shall be greater than or equal to `alignof(P)`. | +| `F::copyability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required copyability of `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`. | ## See Also diff --git a/docs/spec/ProReflection.md b/docs/spec/ProReflection.md index 936771a1..e5e99333 100644 --- a/docs/spec/ProReflection.md +++ b/docs/spec/ProReflection.md @@ -6,7 +6,7 @@ A type `R` meets the *ProReflection* requirements of a type `P` if `R` meets the | Expressions | Semantics | | --------------------------------------------------- | ------------------------------------------------------------ | -| `typename R::reflector_type{std::in_place_type}` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) that constructs a value of type `typename R::reflector_type`, reflecting implementation-defined metadata of type `T`. | +| `typename R::reflector_type(std::in_place_type)` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) that constructs a value of type `typename R::reflector_type`, reflecting implementation-defined metadata of type `T`. | ## See Also diff --git a/docs/spec/README.md b/docs/spec/README.md index 5c6f606e..51f000b2 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -27,7 +27,6 @@ This document provides the API specifications for the C++ library Proxy (version | [`implicit_conversion_dispatch`](implicit_conversion_dispatch/README.md) | Dispatch type for implicit conversion expressions with accessibility | | [`not_implemented` ](not_implemented.md) | Exception thrown by `weak_dispatch` for the default implementation | | [`operator_dispatch`](operator_dispatch/README.md) | Dispatch type for operator expressions with accessibility | -| [`proxiable_ptr_constraints`](proxiable_ptr_constraints.md) | Defines the constraints of a pointer type to instantiate a `proxy` | | [`proxy_indirect_accessor`](proxy_indirect_accessor.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 | @@ -47,7 +46,6 @@ This document provides the API specifications for the C++ library Proxy (version | Name | Description | | --------------------------------------------------- | ------------------------------------------------------------ | -| [`access_proxy`](access_proxy.md) | Accesses a `proxy` object via an accessor | | [`allocate_proxy_shared`](allocate_proxy_shared.md) | Creates a `proxy` object with shared ownership using an allocator | | [`allocate_proxy`](allocate_proxy.md) | Creates a `proxy` object with an allocator | | [`make_proxy_inplace`](make_proxy_inplace.md) | Creates a `proxy` object with strong no-allocation guarantee | @@ -55,7 +53,7 @@ This document provides the API specifications for the C++ library Proxy (version | [`make_proxy_view`](make_proxy_view.md) | Creates a `proxy_view` object | | [`make_proxy`](make_proxy.md) | Creates a `proxy` object potentially with heap allocation | | [`proxy_invoke`](proxy_invoke.md) | Invokes a `proxy` with a specified convention | -| [`proxy_reflect`](proxy_reflect.md) | Acquires reflection information of the underlying pointer type | +| [`proxy_reflect`](proxy_reflect.md) | Acquires reflection information of a contained type | ## Header `` diff --git a/docs/spec/access_proxy.md b/docs/spec/access_proxy.md deleted file mode 100644 index 3fe785d8..00000000 --- a/docs/spec/access_proxy.md +++ /dev/null @@ -1,73 +0,0 @@ -# Function template `access_proxy` - -> Header: `proxy.h` -> Module: `proxy` -> Namespace: `pro::inline v4` - -```cpp -template -proxy& access_proxy(A& a) noexcept; - -template -const proxy& access_proxy(const A& a) noexcept; - -template -proxy&& access_proxy(A&& a) noexcept; - -template -const proxy&& access_proxy(const A&& a) noexcept; -``` - -Accesses a `proxy` object from an [accessor](ProAccessible.md) instantiated from the `proxy`. As per `facade`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing distinct types `Cs`. There shall be a type `C` in `Cs` where `A` is the same type as `typename C::template accessor`. The behavior is undefined if `a` is not instantiated from a `proxy`. - -## Return Value - -A reference to the `proxy` that has instantiated `a`. - -## Notes - -Similar to [`proxy_invoke`](proxy_invoke.md), this function can be used to implement the accessibility of `proxy`. If the facade type `F` is defined with the recommended facilities, it has full accessibility support. Specifically, when: - -- the underlying dispatch type `typename C::dispatch_type` is defined via [macro `PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md), [macro `PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md), or is a specialization of either [`operator_dispatch`](operator_dispatch/README.md) or [`conversion_dispatch`](explicit_conversion_dispatch/README.md), and -- the convention is defined via [`facade_builder`](basic_facade_builder/README.md). - -## Example - -```cpp -#include -#include - -#include - -PRO_DEF_FREE_DISPATCH(FreeToString, std::to_string, ToString); - -struct Stringable : pro::facade_builder // - ::add_convention // - ::build {}; - -int main() { - pro::proxy p = pro::make_proxy(123); - - // Invokes with accessibility API - std::cout << ToString(*p) << "\n"; // Prints "123" - - // How it works behind the scenes - using Accessor = - FreeToString::accessor; - static_assert( - std::is_base_of_v>); - Accessor& a = static_cast(*p); - pro::proxy& p2 = pro::access_proxy(a); - - // Prints "true" because access_proxy converts - // an accessor back to the original proxy - std::cout << std::boolalpha << (&p == &p2) << "\n"; - auto result = pro::proxy_invoke(p2); - std::cout << result << "\n"; // Prints "123" -} -``` - -## See Also - -- [named requirements *ProAccessible*](ProAccessible.md) -- [function template `proxy_invoke`](proxy_invoke.md) diff --git a/docs/spec/allocate_proxy.md b/docs/spec/allocate_proxy.md index 619d179e..8b323add 100644 --- a/docs/spec/allocate_proxy.md +++ b/docs/spec/allocate_proxy.md @@ -38,7 +38,7 @@ Throws any exception thrown by allocation or the constructor of `T`. ## Notes -The implementation of `allocated-ptr` may vary depending on the definition of `F`. Specifically, when `F::constraints.max_size` and `F::constraints.max_align` are not large enough to hold both a pointer to the allocated memory and a copy of the allocator, `allocated-ptr` shall allocate additional storage for the allocator. +The implementation of `allocated-ptr` may vary depending on the definition of `F`. Specifically, when `F::max_size` and `F::max_align` are not large enough to hold both a pointer to the allocated memory and a copy of the allocator, `allocated-ptr` shall allocate additional storage for the allocator. ## Example diff --git a/docs/spec/basic_facade_builder/README.md b/docs/spec/basic_facade_builder/README.md index e326dd5f..7b901519 100644 --- a/docs/spec/basic_facade_builder/README.md +++ b/docs/spec/basic_facade_builder/README.md @@ -12,22 +12,20 @@ constexpr constraint_level default-cl = static_cast( std::numeric_limits>::min()); // exposition only ``` -*default-size* and *default-cl* denote that a field in [`proxiable_ptr_constraints`](../proxiable_ptr_constraints.md) is not specified in the template parameters of a `basic_facade_builder` specialization. In an instantiation of `proxiable_ptr_constraints`, any meaningful value of `max_size` and `max_align` is less than *default-size*; any meaningful value of `copyability`, `relocatability`, and `destructibility` is greater than *default-cl*. +Given a [facade](../facade.md ) type `F`, any meaningful value of `F::max_size` and `F::max_align` is less than *default-size*; any meaningful value of `F::copyability`, `F::relocatability`, and `F::destructibility` is greater than *default-cl*. ```cpp -template +template class basic_facade_builder; -using facade_builder = basic_facade_builder, std::tuple<>, - proxiable_ptr_constraints{ - .max_size = default-size, - .max_align = default-size, - .copyability = default-cl, - .relocatability = default-cl, - .destructibility = default-cl}>; +using facade_builder = + basic_facade_builder, std::tuple<>, default-size, default-size, + default-cl, default-cl, default-cl>; ``` -`class Cs`, `class Rs`, and `proxiable_ptr_constraints C` are the template parameters of `basic_facade_builder`. `basic_facade_builder` provides a member type `build` that compiles the template parameters into a [`facade`](../facade.md) type. The template parameters can be modified via various member alias templates that specify `basic_facade_builder` with the modified template parameters. +`basic_facade_builder` provides a member type `build` that compiles the template parameters into a [`facade`](../facade.md) type. The template parameters can be modified via various member alias templates that specify `basic_facade_builder` with the modified template parameters. ## Member Types @@ -42,11 +40,11 @@ using facade_builder = basic_facade_builder, std::tuple<>, | [`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_reflection`
`add_indirect_reflection`
`add_direct_reflection`](add_reflection.md) | Adds a reflection to the template parameters | -| [`restrict_layout`](restrict_layout.md) | Specifies maximum `max_size` and `max_align` of `C` in the template parameters | +| [`restrict_layout`](restrict_layout.md) | Specifies maximum `MaxSize` and `MaxAlign` in the template parameters | | [`support`](support.md) | Specifies a custom skill | -| [`support_copy`](support_copy.md) | Specifies minimum `copyability` of `C` in the template parameters | -| [`support_destruction`](support_destruction.md) | Specifies minimum `destructibility` of `C` in the template parameters | -| [`support_relocation`](support_relocation.md) | Specifies minimum `relocatability` of `C` in the template parameters | +| [`support_copy`](support_copy.md) | Specifies minimum `Copyability` in the template parameters | +| [`support_destruction`](support_destruction.md) | Specifies minimum `Destructibility` in the template parameters | +| [`support_relocation`](support_relocation.md) | Specifies minimum `Relocatability` in the template parameters | ## Member Functions diff --git a/docs/spec/basic_facade_builder/add_convention.md b/docs/spec/basic_facade_builder/add_convention.md index 977d1863..7f540cd6 100644 --- a/docs/spec/basic_facade_builder/add_convention.md +++ b/docs/spec/basic_facade_builder/add_convention.md @@ -11,7 +11,7 @@ template requires(/* see below */) using add_direct_convention = basic_facade_builder; ``` -The alias templates `add_convention`, `add_indirect_convention`, and `add_direct_convention` of `basic_facade_builder` add convention types to the template parameters. The expression inside `requires` is equivalent to `sizeof...(Os) > 0u` and each type in `Os` meets the [*ProOverload* requirements](../ProOverload.md). Let `F` be a facade type, +The alias templates `add_convention`, `add_indirect_convention`, and `add_direct_convention` of `basic_facade_builder` add convention types to the template parameters. The expression inside `requires` is equivalent to `sizeof...(Os) > 0u` and each type in `Os` meets the [*ProOverload* requirements](../ProOverload.md). Let `F` be a facade type, - `add_convention` is equivalent to `add_indirect_convention`. - `add_indirect_convention` merges an implementation-defined convention type `IC` into `Cs`, where: diff --git a/docs/spec/basic_facade_builder/add_facade.md b/docs/spec/basic_facade_builder/add_facade.md index f5520fce..645cc53e 100644 --- a/docs/spec/basic_facade_builder/add_facade.md +++ b/docs/spec/basic_facade_builder/add_facade.md @@ -5,7 +5,16 @@ template using add_facade = basic_facade_builder; ``` -The alias template `add_facade` of `basic_facade_builder` adds a [facade](../facade.md) type into the template parameters. It merges `typename F::convention_types` into `Cs`, `typename F::reflection_types` into `Rs`, and `F::constraints` into `C`. Optionally, it adds a convention for implicit upward conversion into `Cs` when `WithUpwardConversion` is `true`. +The alias template `add_facade` of `basic_facade_builder` adds a [facade](../facade.md) type into the template parameters. Specifically, it + +- merges `typename F::convention_types` into `Cs`, and +- merges `typename F::reflection_types` into `Rs`, and +- sets `MaxSize` to `std::min(MaxSize, F::max_size)`, and +- sets `MaxAlign` to `std::min(MaxAlign, F::max_align)`, and +- sets `Copyability` to `std::max(Copyability, F::copyability)`, and +- sets `Relocatability` to `std::max(Relocatability, F::relocatability)`, and +- sets `Destructibility` to `std::max(Destructibility, F::destructibility)`, and +- optionally, adds a convention for implicit upward conversion into `Cs` when `WithUpwardConversion` is `true`. ## Notes diff --git a/docs/spec/basic_facade_builder/add_reflection.md b/docs/spec/basic_facade_builder/add_reflection.md index 45e78a71..9bd33d46 100644 --- a/docs/spec/basic_facade_builder/add_reflection.md +++ b/docs/spec/basic_facade_builder/add_reflection.md @@ -11,7 +11,7 @@ template using add_direct_reflection = basic_facade_builder; ``` -The alias templates `add_reflection`, `add_indirect_reflection` and `add_direct_reflection` of `basic_facade_builder` add reflection types to the template parameters. Specifically, +The alias templates `add_reflection`, `add_indirect_reflection` and `add_direct_reflection` of `basic_facade_builder` add reflection types to the template parameters. Specifically, - `add_reflection` is equivalent to `add_indirect_reflection`. - `add_indirect_reflection` merges an implementation-defined reflection type `Refl` into `Rs`, where: @@ -43,23 +43,15 @@ public: constexpr explicit LayoutReflector(std::in_place_type_t) : Size(sizeof(T)), Align(alignof(T)) {} - template + template struct accessor { - friend std::size_t - SizeOf(const std::conditional_t, - pro::proxy_indirect_accessor>& - self) noexcept { - const LayoutReflector& refl = - pro::proxy_reflect(pro::access_proxy(self)); + friend std::size_t SizeOf(const P& self) noexcept { + const LayoutReflector& refl = pro::proxy_reflect(self); return refl.Size; } - friend std::size_t - AlignOf(const std::conditional_t, - pro::proxy_indirect_accessor>& - self) noexcept { - const LayoutReflector& refl = - pro::proxy_reflect(pro::access_proxy(self)); + friend std::size_t AlignOf(const P& self) noexcept { + const LayoutReflector& refl = pro::proxy_reflect(self); return refl.Align; } }; diff --git a/docs/spec/basic_facade_builder/build.md b/docs/spec/basic_facade_builder/build.md index 7c7babf0..5a551830 100644 --- a/docs/spec/basic_facade_builder/build.md +++ b/docs/spec/basic_facade_builder/build.md @@ -4,34 +4,7 @@ using build = /* see below */; ``` -Specifies a [facade](../facade.md) type deduced from the template parameters of `basic_facade_builder`. Specifically, - -- `typename build::convention_types` is defined as `Cs`, and -- `typename build::reflection_types` is defined as `Rs`, and -- `build::constraints` is a [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`proxiable_ptr_constraints`](../proxiable_ptr_constraints.md) that defines constraints to the pointer types, and -- `build::constraints.max_size` is `C::max_size` if defined by [`restrict_layout`](restrict_layout.md), otherwise `sizeof(void*) * 2u` when `C::max_size` is *default-size*, and -- `build::constraints.max_align` is `C::max_align` if defined by [`restrict_layout`](restrict_layout.md), otherwise `alignof(void*)` when `C::max_align` is *default-size*, and -- `build::constraints.copyability` is `C::copyability` if defined by [`support_copy`](support_copy.md), otherwise `constraint_level::none` when `C::copyability` is *default-cl*, and -- `build::constraints.relocatability` is `C::relocatability` if defined by [`support_rellocation`](support_relocation.md), otherwise `constraint_level::nothrow` when `C::relocatability` is *default-cl*, and -- `build::constraints.destructibility` is `C::destructibility` if defined by [`support_destruction`](support_destruction.md), otherwise `constraint_level::nothrow` when `C::destructibility` is *default-cl*. - -The definition of type `build` makes use of the following exposition-only function: - -```cpp -consteval proxiable_ptr_constraints normalize(proxiable_ptr_constraints value) { - if (value.max_size == default-size) - { value.max_size = sizeof(void*) * 2u; } - if (value.max_align == default-size) - { value.max_align = alignof(void*); } - if (value.copyability == default-cl) - { value.copyability = constraint_level::none; } - if (value.relocatability == default-cl) - { value.relocatability = constraint_level::nothrow; } - if (value.destructibility == default-cl) - { value.destructibility = constraint_level::nothrow; } - return value; -} -``` +Specifies a [facade](../facade.md) type deduced from the template parameters of `basic_facade_builder`. ## Member Types @@ -42,15 +15,19 @@ consteval proxiable_ptr_constraints normalize(proxiable_ptr_constraints value) { ## Member Constants -| Name | Definition | -| ---------------------------------- | -------------- | -| `constraints` [static] [constexpr] | `normalize(C)` | +| Name | Definition | +| -------------------------------------- | ------------------------------------------------------------ | +| `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` | +| `destructibility` [static] [constexpr] | `Destructibility == default-cl ? constraint_level::nothrow : Destructibility` | ## Notes It is encouraged to inherit `build` with an empty `struct` before specifying a [`proxy`](../proxy/README.md), rather than `using` or `typedef` the `build` type into an alias, to improve compilation performance. -The default values of the fields of [`proxiable_ptr_constraints`](../proxiable_ptr_constraints.md) are based on our engineering practices. The default values of `max_size` and `max_alignment` are usually sufficient for many implementations of [fancy pointers](https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers), such as [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr), [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr), and [`boost::interprocess::offset_ptr`](https://www.boost.org/doc/libs/1_85_0/doc/html/interprocess/offset_ptr.html). A larger combination of size and alignment ensures better compatibility with the implementation of the underlying pointers and reduces heap allocation when the element type fits in the buffer (see [function template `make_proxy`](../make_proxy.md)), at the cost of making the corresponding [`proxy`](../proxy/README.md) objects larger. +The default values of the member constants are based on our engineering practices. The default values of `max_size` and `max_alignment` are usually sufficient for many implementations of [fancy pointers](https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers), such as [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr), [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr), and [`boost::interprocess::offset_ptr`](https://www.boost.org/doc/libs/1_85_0/doc/html/interprocess/offset_ptr.html). A larger combination of size and alignment ensures better compatibility with the implementation of the underlying pointers and reduces heap allocation when the element type fits in the buffer (see [function template `make_proxy`](../make_proxy.md)), at the cost of making the corresponding [`proxy`](../proxy/README.md) objects larger. ## Example diff --git a/docs/spec/basic_facade_builder/restrict_layout.md b/docs/spec/basic_facade_builder/restrict_layout.md index da7581ba..74fdd687 100644 --- a/docs/spec/basic_facade_builder/restrict_layout.md +++ b/docs/spec/basic_facade_builder/restrict_layout.md @@ -6,11 +6,11 @@ template using restrict_layout = basic_facade_builder; ``` -The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters, specifically `C::max_size` and `C::max_align`. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t`). After applying the restriction, `C::max_size` becomes `std::min(C::max_size, PtrSize)`, and `C::max_align` becomes `std::min(C::max_align, PtrAlign)`. +The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t`). After applying the restriction, `MaxSize` becomes `std::min(MaxSize, PtrSize)`, and `MaxAlign` becomes `std::min(C::max_align, MaxAlign)`. ## Notes -If no layout restriction is applied before specifying [`build`](build.md), the default value of `build::constraints.max_size` is `sizeof(void*) * 2`, and the default value of `build::constraints.max_align` is `alignof(void*)`. +If no layout restriction is applied before specifying [`build`](build.md), the default value of `build::max_size` is `sizeof(void*) * 2`, and the default value of `build::max_align` is `alignof(void*)`. ## Example diff --git a/docs/spec/basic_facade_builder/support.md b/docs/spec/basic_facade_builder/support.md index f8be42f6..fcfbad43 100644 --- a/docs/spec/basic_facade_builder/support.md +++ b/docs/spec/basic_facade_builder/support.md @@ -8,7 +8,7 @@ template