diff --git a/CMakeLists.txt b/CMakeLists.txt index e1103b3..30f1018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(msft_proxy VERSION 3.1.0 LANGUAGES CXX) +project(msft_proxy VERSION 3.2.0 LANGUAGES CXX) add_library(msft_proxy INTERFACE) target_compile_features(msft_proxy INTERFACE cxx_std_20) target_include_directories(msft_proxy INTERFACE $ diff --git a/README.md b/README.md index ef9ef57..f44b9a5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,56 @@ Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.githu Let's get started with the following "Hello World" example: ```cpp +#include +#include +#include + +#include "proxy.h" + +struct Formattable : pro::facade_builder + ::support_format + ::build {}; + +int main() { + std::string str = "Hello World"; + pro::proxy p1 = &str; + std::cout << std::format("p1 = {}\n", *p1); // Prints: "p1 = Hello World" + + pro::proxy p2 = std::make_unique(123); + std::cout << std::format("p2 = {}\n", *p2); // Prints: "p2 = 123" + + pro::proxy p3 = pro::make_proxy(3.14159); + std::cout << std::format("p3 = {:.2f}\n", *p3) << "\n"; // Prints: "p3 = 3.14" +} +``` + +Here is a step-by-step explanation: + +- `#include `: For [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format). +- `#include `: For [`std::cout`](https://en.cppreference.com/w/cpp/io/cout). +- `#include `: For [`std::string`](https://en.cppreference.com/w/cpp/string/basic_string). +- `#include "proxy.h"`: For the "Proxy" library. Most of the facilities of the library are defined in namespace `pro`. If the library is consumed via [vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/overview) or [conan](https://conan.io/), this line should be changed into `#include `. +- `struct Formattable : pro::facade_builder ... ::build {}`: Defines a facade type `Formattable`. The term "facade", formally defined as the [*ProFacade* requirements](https://microsoft.github.io/proxy/docs/ProFacade.html), is how the "Proxy" library models runtime abstraction. Specifically, + - [`pro::facade_builder`](https://microsoft.github.io/proxy/docs/basic_facade_builder.html): Provides capability to build a facade type at compile-time. + - [`support_format`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_format.html): Specifies the capability of formatting (via [standard formatting functions](https://en.cppreference.com/w/cpp/utility/format)). + - [`build`](https://microsoft.github.io/proxy/docs/basic_facade_builder/build.html): Builds the context into a facade type. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. `p1` behaves like a raw pointer, and does not have ownership of the underlying `std::string`. If the lifetime of `str` ends before `p1`, `p1` becomes dangling. +- `std::format("p1 = {}\n", *p1)`: This is how it works. `*p1` is formatted as "Hello World" because the capability was defined in the facade `Formattable`, so it works as if by calling `std::format("p1 = {}\n", str)`. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. Different from `p1`, `p2` has ownership of the underlying `int` because it is instantiated from a value of `std::unique_ptr`, and will call the destructor of `std::unique_ptr` when `p2` is destroyed, while `p1` does not have ownership of the underlying `int` because it is instantiated from a raw pointer. `p1` and `p2` are of the same type `pro::proxy`, which means you can have a function that returns `pro::proxy` without exposing any information about the implementation details to its caller. +- `std::format("p2 = {}\n", *p2)`: Formats `*p2` as "123" with no surprises. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/docs/make_proxy.html)`(3.14159)`: Creates a `proxy` from a `double` without specifying the underlying pointer type. Specifically, + - Similar with `p2`, `p3` also has ownership of the underlying `double` value, but can effectively avoid heap allocation. + - Since the size of the underlying type (`double`) is known to be small (on major 32- or 64-bit platforms), [`pro::make_proxy`](https://microsoft.github.io/proxy/docs/make_proxy.html) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://microsoft.github.io/proxy/docs/make_proxy_inplace.html), which guarantees no heap allocation. + - The "Proxy" library explicitly defines when heap allocation occurs or not to avoid users falling into performance hell, which is different from [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and other existing polymorphic wrappers in the standard. +- `std::format("p3 = {:.2f}\n", *p3)`: Formats `*p3` as "3.14" as per the [standard format specification](https://en.cppreference.com/w/cpp/utility/format/spec) with no surprises. +- When `main` returns, `p2` and `p3` will destroy the underlying objects, while `p1` does nothing because it holds a raw pointer that does not have ownership of the underlying `std::string`. + +### Hello World (Stream Version) + +In the previous "Hello Word" example, we demonstrated how `proxy` could manage different types of objects and be formatted with `std::format`. While `std::format` is not the only option to print objects in C++, can we simply make `proxy` work with `std::cout`? The answer is "yes". The previous example is equivalent to the following implementation: + +```cpp +#include #include #include @@ -51,42 +101,40 @@ int main() { pro::proxy p2 = std::make_unique(123); std::cout << "p2 = " << *p2 << "\n"; // Prints: "p2 = 123" - pro::proxy p3 = pro::make_proxy(3.14); - std::cout << "p3 = " << *p3 << "\n"; // Prints: "p3 = 3.14" + pro::proxy p3 = pro::make_proxy(3.14159); + std::cout << "p3 = " << std::fixed << std::setprecision(2) << *p3 << "\n"; // Prints: "p3 = 3.14" } ``` Here is a step-by-step explanation: +- `#include `: For [`std::setprecision`](https://en.cppreference.com/w/cpp/io/manip/setprecision). - `#include `: For [`std::cout`](https://en.cppreference.com/w/cpp/io/cout). - `#include `: For [`std::string`](https://en.cppreference.com/w/cpp/string/basic_string). -- `#include "proxy.h"`: For the "Proxy" library. Most of the facilities of the library are defined in namespace `pro`. If the library is consumed via [vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/overview) or [conan](https://conan.io/), this line should be changed into `#include `. -- `struct Streamable : pro::facade_builder ... ::build {}`: Defines a facade type `Streamable`. The term "facade", formally defined as the [*ProFacade* requirements](https://microsoft.github.io/proxy/docs/ProFacade.html), is how the "Proxy" library models runtime abstraction. Specifically, - - [`pro::facade_builder`](https://microsoft.github.io/proxy/docs/basic_facade_builder.html): Provides capability to build a facade type at compile-time. +- `#include "proxy.h"`: For the "Proxy" library. +- `struct Streamable : pro::facade_builder ... ::build {}`: Defines a facade type `Streamable`. Specifically, + - [`pro::facade_builder`](https://microsoft.github.io/proxy/docs/basic_facade_builder.html): Gets prepared to build another facade. - [`add_convention`](https://microsoft.github.io/proxy/docs/basic_facade_builder/add_convention.html): Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context. - [`pro::operator_dispatch`](https://microsoft.github.io/proxy/docs/operator_dispatch.html)`<"<<", true>`: Specifies a dispatch for operator `<<` expressions where the primary operand (`proxy`) is on the right-hand side (specified by the second template parameter `true`). Note that polymorphism in the "Proxy" library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages. - `std::ostream&(std::ostream& out) const`: The signature of the calling convention, similar with [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function). `const` specifies that the primary operand is `const`. - [`build`](https://microsoft.github.io/proxy/docs/basic_facade_builder/build.html): Builds the context into a facade type. -- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. `p1` behaves like a raw pointer, and does not have ownership of the underlying `std::string`. If the lifetime of `str` ends before `p1`, `p1` becomes dangling. -- `std::cout << *p1`: This is how it works. It prints "Hello World" because the calling convention is defined in the facade `Streamable`, so it works as if by calling `std::cout << str`. -- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. Different from `p1`, `p2` has ownership of the underlying `int` because it is instantiated from a value of `std::unique_ptr`, and will call the destructor of `std::unique_ptr` when `p2` is destroyed, while `p1` does not have ownership of the underlying `int` because it is instantiated from a raw pointer. `p1` and `p2` are of the same type `pro::proxy`, which means you can have a function that returns `pro::proxy` without exposing any information about the implementation details to its caller. -- `std::cout << *p2`: Prints "123" with no surprise. -- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/docs/make_proxy.html)`(3.14)`: Creates a `proxy` from a `double` without specifying the underlying pointer type. Specifically, - - Similar with `p2`, `p3` also has ownership of the underlying `double` value, but can effectively avoid heap allocation. - - Since the size of the underlying type (`double`) is known to be small (on major 32- or 64-bit platforms), [`pro::make_proxy`](https://microsoft.github.io/proxy/docs/make_proxy.html) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://microsoft.github.io/proxy/docs/make_proxy_inplace.html), which guarantees no heap allocation. - - The "Proxy" library explicitly defines when heap allocation occurs or not to avoid users falling into performance hell, which is different from [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and other existing polymorphic wrappers in the standard. -- `std::cout << *p3`: Prints "3.14" with no surprise. -- When `main` returns, `p2` and `p3` will destroy the underlying objects, while `p1` does nothing because it holds a raw pointer that does not have ownership of the underlying `std::string`. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. +- `std::cout << *p1`: It prints "Hello World" because the calling convention is defined in the facade `Streamable`, so it works as if by calling `std::cout << str`. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p2 = `[`std::make_unique`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)`(123)`: Creates a [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)`` and converts to a `proxy`. +- `std::cout << *p2`: Prints "123" with no surprises. +- [`pro::proxy`](https://microsoft.github.io/proxy/docs/proxy.html)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/docs/make_proxy.html)`(3.14)`: Creates a `proxy` from a `double`. +- `std::cout << std::fixed << std::setprecision(2) << *p3;`: Prints "3.14" with no surprises. ### More Expressions -In addition to the operator expressions demonstrated in the previous example, the library supports almost all forms of expressions in C++ and can make them polymorphic. Specifically, +In addition to the operator expressions demonstrated in the previous examples, the library supports almost all forms of expressions in C++ and can make them polymorphic. Specifically, -- [macro `PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_MEM_DISPATCH.html): Defines a dispatch type for member function call expressions. -- [macro `PRO_DEF_FREE_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_FREE_DISPATCH.html): Defines a dispatch type for free function call expressions. +- [macro `PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_MEM_DISPATCH.html): Defines a dispatch type for member function call expressions, providing accessibility as member functions. +- [macro `PRO_DEF_FREE_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_FREE_DISPATCH.html): Defines a dispatch type for free function call expressions, providing accessibility as free functions. +- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.html): Defines a dispatch type for free function call expressions, providing accessibility as member functions. - [class template `pro::operator_dispatch`](https://microsoft.github.io/proxy/docs/operator_dispatch.html): Dispatch type for operator expressions. -- [class template `pro::conversion_dispatch`](https://microsoft.github.io/proxy/docs/conversion_dispatch.html): Dispatch type for conversion expressions. +- [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](https://microsoft.github.io/proxy/docs/explicit_conversion_dispatch.html) and [class `implicit_conversion_dispatch`](https://microsoft.github.io/proxy/docs/implicit_conversion_dispatch.html): Dispatch type for conversion expressions. Note that some facilities are provided as macro, because C++ templates today do not support generating a function with an arbitrary name. Here is another example that makes member function call expressions polymorphic: @@ -157,10 +205,12 @@ The "Proxy" library is a self-contained solution for runtime polymorphism in C++ - **Overloading**: [`facade_builder::add_convention`](https://microsoft.github.io/proxy/docs/basic_facade_builder/add_convention.html) is more powerful than demonstrated above. It can take any number of overload types (formally, any type meeting the [*ProOverload* requirements](https://microsoft.github.io/proxy/docs/ProOverload.html)) and perform standard overload resolution when invoking a `proxy`. - **Facade composition**: [`facade_builder::add_facade`](https://microsoft.github.io/proxy/docs/basic_facade_builder/add_facade.html) allows flexible composition of different abstractions. -- **Weak dispatch**: When an object does not implement a convention, and we do not want it to trigger a hard compile error, it is allowed to define a "weak dispatch" with [macro `PRO_DEF_WEAK_DISPATCH`](https://microsoft.github.io/proxy/docs/PRO_DEF_WEAK_DISPATCH.html) from an existing dispatch type and a default implementation. - **Allocator awareness**: [function template `allocate_proxy`](https://microsoft.github.io/proxy/docs/allocate_proxy.html) is able to create a `proxy` from a value with any custom allocator. In C++11, [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) and [`std::packaged_task`](https://en.cppreference.com/w/cpp/thread/packaged_task) had constructors that accepted custom allocators for performance tuning, but these were [removed in C++17](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r1.html) because "the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment". These issues do not apply to `allocate_proxy`. - **Configurable constraints**: [`facade_builder`](https://microsoft.github.io/proxy/docs/basic_facade_builder.html) provides full support for constraints configuration, including memory layout (by [`restrict_layout`](https://microsoft.github.io/proxy/docs/basic_facade_builder/restrict_layout.html)), copyability (by [`support_copy`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_copy.html)), relocatability (by [`support_relocation`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_relocation.html)), and destructibility (by [`support_destruction`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_destruction.html)). - **Reflection**: `proxy` supports type-based compile-time reflection for runtime queries. Please refer to [`facade_builder::add_reflection`](https://microsoft.github.io/proxy/docs/basic_facade_builder/add_reflection.html) and [function template `proxy_reflect`](https://microsoft.github.io/proxy/docs/proxy_reflect.html) for more details. +- **Non-owning proxy**: Although `proxy` can manage the lifetime of an object effectively, similar to a smart pointer, we sometimes want to dereference it before passing to a non-owning context. This has been implemented as an extension since 3.2. Please refer to [class template `observer_facade`, alias template `proxy_view`](https://microsoft.github.io/proxy/docs/observer_facade.html) and [`facade_builder::add_view`](https://microsoft.github.io/proxy/docs/basic_facade_builder/add_view.html) for more details. +- **RTTI**: [RTTI (run-time type information)](https://en.wikipedia.org/wiki/Run-time_type_information) provides "weak" reflection capability in C++ since the last century. Although it is not as powerful as reflection in some other languages (like `Object.GetType()` in C# or `Object.getClass()` in Java), it offers the basic infrastructure for type-safe casting at runtime. Since 3.2, "RTTI for `proxy`" has been implemented as an extension and allows users to opt-in for each facade definition. Please refer to [`facade_builder::support_rtti`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_rtti.html) for more details. +- **Weak dispatch**: When an object does not implement a convention, and we do not want it to trigger a hard compile error, it is allowed to specify a [`weak_dispatch`](https://microsoft.github.io/proxy/docs/weak_dispatch.html) that throws when invoked. ## Minimum Requirements for Compilers @@ -183,9 +233,11 @@ ctest --test-dir build -j ## Related Resources +- January, 2025: [Published ISO C++ proposal P3086R3: Proxy: A Pointer-Semantics-Based Polymorphism Library](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3086r3.pdf) +- January, 2025: [Published ISO C++ proposal P3584R0: Enrich Facade Creation Facilities for the Pointer-Semantics-Based Polymorphism Library - Proxy](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3584r0.pdf) - November, 2024: [Analyzing the Performance of the “Proxy” Library](https://devblogs.microsoft.com/cppblog/analyzing-the-performance-of-the-proxy-library/) +- September, 2024: [Published ISO C++ proposal P3401R0: Enrich Creation Functions for the Pointer-Semantics-Based Polymorphism Library - Proxy](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3401r0.pdf) - September, 2024: [Announcing the Proxy 3 Library for Dynamic Polymorphism](https://devblogs.microsoft.com/cppblog/announcing-the-proxy-3-library-for-dynamic-polymorphism/) -- April, 2024: [Published ISO C++ proposal P3086R2: Proxy: A Pointer-Semantics-Based Polymorphism Library](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3086r2.pdf) - August, 2022: [proxy: Runtime Polymorphism Made Easier Than Ever](https://devblogs.microsoft.com/cppblog/proxy-runtime-polymorphism-made-easier-than-ever/) ## Contributing diff --git a/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md b/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md index 1082c8f..b8965e6 100644 --- a/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md +++ b/docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md @@ -1,7 +1,7 @@ # Macro `PRO_DEF_FREE_AS_MEM_DISPATCH` ```cpp -#define PRO_DEF_FREE_AS_MEM_DISPATCH // see below +#define PRO_DEF_FREE_AS_MEM_DISPATCH // since 3.1, see below ``` Macro `PRO_DEF_FREE_AS_MEM_DISPATCH` defines dispatch types for free function expressions with accessibility via a member function. It supports two syntaxes: @@ -21,7 +21,7 @@ PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name); ```cpp struct dispatch_name { template - decltype(auto) operator()(T&& self, Args&&... args) + decltype(auto) operator()(T&& self, Args&&... args) const noexcept(noexcept(func_name(std::forward(self), std::forward(args)...))) requires(requires { func_name(std::forward(self), std::forward(args)...); }) { return func_name(std::forward(self), std::forward(args)...); diff --git a/docs/PRO_DEF_FREE_DISPATCH.md b/docs/PRO_DEF_FREE_DISPATCH.md index e873d77..6819f16 100644 --- a/docs/PRO_DEF_FREE_DISPATCH.md +++ b/docs/PRO_DEF_FREE_DISPATCH.md @@ -21,7 +21,7 @@ PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, accessibility_func_name); ```cpp struct dispatch_name { template - decltype(auto) operator()(T&& self, Args&&... args) + decltype(auto) operator()(T&& self, Args&&... args) const noexcept(noexcept(func_name(std::forward(self), std::forward(args)...))) requires(requires { func_name(std::forward(self), std::forward(args)...); }) { return func_name(std::forward(self), std::forward(args)...); diff --git a/docs/PRO_DEF_MEM_DISPATCH.md b/docs/PRO_DEF_MEM_DISPATCH.md index 2da23c4..050b2de 100644 --- a/docs/PRO_DEF_MEM_DISPATCH.md +++ b/docs/PRO_DEF_MEM_DISPATCH.md @@ -21,7 +21,7 @@ PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name); ```cpp struct dispatch_name { template - decltype(auto) operator()(T&& self, Args&&... args) + decltype(auto) operator()(T&& self, Args&&... args) const noexcept(noexcept(std::forward(self).func_name(std::forward(args)...))) requires(requires { std::forward(self).func_name(std::forward(args)...); }) { return std::forward(self).func_name(std::forward(args)...); diff --git a/docs/PRO_DEF_WEAK_DISPATCH.md b/docs/PRO_DEF_WEAK_DISPATCH.md index 9dc4063..8b69b6a 100644 --- a/docs/PRO_DEF_WEAK_DISPATCH.md +++ b/docs/PRO_DEF_WEAK_DISPATCH.md @@ -1,9 +1,11 @@ # Macro `PRO_DEF_WEAK_DISPATCH` ```cpp -#define PRO_DEF_WEAK_DISPATCH // see below +#define PRO_DEF_WEAK_DISPATCH // deprecated since 3.2, see below ``` +⚠️ Macro `PRO_DEF_WEAK_DISPATCH` has been replaced by class template `weak_dispatch` since 3.2, and may be removed in a future version. + Macro `PRO_DEF_WEAK_DISPATCH` defines a "weak dispatch" type with a default implementation. It supports the following syntax: ```cpp @@ -16,7 +18,7 @@ Defines a class named `dispatch_name` that inherits `existing_dispatch` and prov struct dispatch_name : existing_dispatch { using existing_dispatch::operator(); template - decltype(auto) operator()(std::nullptr_t, Args&&... args) + decltype(auto) operator()(std::nullptr_t, Args&&... args) const noexcept(noexcept(default_func_name(std::forward(args)...))) requires(requires { default_func_name(std::forward(args)...); }) { return default_func_name(std::forward(args)...); @@ -68,4 +70,5 @@ int main() { ## See Also +- [class template `weak_dispatch`](weak_dispatch.md) - [named requirements *ProDispatch*](ProDispatch.md) diff --git a/docs/ProOverload.md b/docs/ProOverload.md index ca01562..f4e55fc 100644 --- a/docs/ProOverload.md +++ b/docs/ProOverload.md @@ -12,7 +12,7 @@ A type `O` meets the *ProOverload* requirements if it matches one of the followi | `R(Args...) && noexcept` | | `R(Args...) const` | | `R(Args...) const noexcept` | -| `R(Args...) cosnt&` | +| `R(Args...) const&` | | `R(Args...) const& noexcept` | | `R(Args...) const&&` | | `R(Args...) const&& noexcept` | diff --git a/docs/access_proxy.md b/docs/access_proxy.md index c36ff8d..bd51ebc 100644 --- a/docs/access_proxy.md +++ b/docs/access_proxy.md @@ -18,7 +18,7 @@ Accesses a `proxy` object from an [accessor](ProAccessible.md) instantiated from ## Return Value -A reference to the `proxy` that instantiates `a`. +A reference to the `proxy` that has instantiated `a`. ## Notes diff --git a/docs/bad_proxy_cast.md b/docs/bad_proxy_cast.md new file mode 100644 index 0000000..bbe4756 --- /dev/null +++ b/docs/bad_proxy_cast.md @@ -0,0 +1,15 @@ +# Class `bad_proxy_cast` + +```cpp +class bad_proxy_cast : public std::bad_cast; +``` + +A type of object to be thrown by the value-returning forms of [`proxy_cast`](basic_facade_builder/support_rtti.md) on failure. + +## Member Functions + +| Name | Description | +| ------------- | ------------------------------------ | +| (constructor) | constructs a `bad_proxy_cast` object | +| (destructor) | destroys a `bad_proxy_cast` object | +| `what` | returns the explanatory string | diff --git a/docs/basic_facade_builder.md b/docs/basic_facade_builder.md index d080ec9..b3285bd 100644 --- a/docs/basic_facade_builder.md +++ b/docs/basic_facade_builder.md @@ -25,23 +25,83 @@ using facade_builder = basic_facade_builder, std::tuple<>, `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. +## Notes + +The design of `basic_facade_builder` utilizes template metaprogramming techniques. We recommend the following 2 guidelines when using `basic_facade_builder` to define a facade type. + +- **Define a type for each facade.** + +For example, when defining a `Formattable` facade, the following two definitions are both syntactically correct: + +```cpp +// (1) Recommended +struct Formattable : pro::facade_builder + ::support_format + ::build {}; + +// (2) Discouraged +using Formattable = pro::facade_builder + ::support_format + ::build; +``` + +Definition `(2)` is a type alias, its "real" type may have a long name, and the type evaluation may be executed for multiple times even when compiling a single source file. Although the two type definitions are equivalent at runtime, definitions like `(2)` may significantly reduce compilation performance. Therefore, it is recommended always to define a facade as a type with inheritance, similar to definition `(1)`. + +- **Use the `template` keyword on demand when defining a facade template.** + +Consider the following facade template definitions: + +```cpp +template +struct MovableCallable : pro::facade_builder + ::add_convention, Os...> + ::build {}; + +template +struct CopyableCallable : pro::facade_builder + ::add_facade> + ::support_copy + ::build {}; +``` + +Although GCC can usually compile the code above, it does not adhere to the C++ standard syntax, and as a result, it won't compile with Clang or MSVC ([live demo](https://godbolt.org/z/Gen74qY9r)). This is because type `add_facade>` depends on the template parameters, and an explicit `template` is required when specifying its member alias template `support_copy`. To fix the code, we could either add the keyword `template` before `support_copy`, or simply swap `add_facade` and `support_copy`. For instance: + +```cpp +template +struct CopyableCallable : pro::facade_builder + ::add_facade> + ::template support_copy + ::build {}; + +// Or + +template +struct CopyableCallable : pro::facade_builder + ::support_copy + ::add_facade> + ::build {}; +``` + ## Member Types -| Name | Description | -| ---------------------------------------- | ------------------------------------------------------------ | -| [`build`](basic_facade_builder/build.md) | Specifies a [`facade`](facade.md) type deduced from the template parameters of the `basic_facade_builder` | +| Name | Description | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| [`build`](basic_facade_builder/build.md) | Specifies a [`facade`](facade.md) type deduced from the template parameters of the `basic_facade_builder` | +| [`support_format`
`support_wformat`](basic_facade_builder/support_format.md)
*(since 3.2)* | Specifies the capability of formatting (via [formatting functions](https://en.cppreference.com/w/cpp/utility/format)) to the template parameters | +| [`support_rtti`
`support_indirect_rtti`
`support_direct_rtti`](basic_facade_builder/support_rtti.md)
*(since 3.2)* | Specifies the capability of RTTI (via `proxy_cast` and `proxy_typeid`) to the template parameters | ## Member Alias Templates | Name | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | | [`add_convention`
`add_indirect_convention`
`add_direct_convention`](basic_facade_builder/add_convention.md) | Adds a convention to the template parameters | -| [`add_reflection`
`add_indirect_reflection`
`add_direct_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters | | [`add_facade`](basic_facade_builder/add_facade.md) | Adds a facade to the template parameters | +| [`add_reflection`
`add_indirect_reflection`
`add_direct_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters | +| [`add_view` ](basic_facade_builder/support_view.md)
*(since 3.2)* | Specifies the capability of implicit conversion to `proxy_view` to the template parameters | | [`restrict_layout`](basic_facade_builder/restrict_layout.md) | Specifies maximum `max_size` and `max_align` of `C` in the template parameters | | [`support_copy`](basic_facade_builder/support_copy.md) | Specifies minimum `copyability` of `C` in the template parameters | -| [`support_relocation`](basic_facade_builder/support_relocation.md) | Specifies minimum `relocatability` of `C` in the template parameters | | [`support_destruction`](basic_facade_builder/support_destruction.md) | Specifies minimum `destructibility` of `C` in the template parameters | +| [`support_relocation`](basic_facade_builder/support_relocation.md) | Specifies minimum `relocatability` of `C` in the template parameters | ## Member Functions diff --git a/docs/basic_facade_builder/build.md b/docs/basic_facade_builder/build.md index 39d5df3..d365fa0 100644 --- a/docs/basic_facade_builder/build.md +++ b/docs/basic_facade_builder/build.md @@ -4,7 +4,7 @@ using build = /* see below */; ``` -Specifies a [facade](facade.md) type deduced from the template parameters of `basic_facade_builder`. Specifically, +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 @@ -50,7 +50,7 @@ consteval proxiable_ptr_constraints normalize(proxiable_ptr_constraints value) { It is encouraged to inherit `build` with an empty `struct` before specifying a [`proxy`](../proxy.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.md) objects larger. +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.md) objects larger. ## Example diff --git a/docs/basic_facade_builder/support_format.md b/docs/basic_facade_builder/support_format.md new file mode 100644 index 0000000..fe01686 --- /dev/null +++ b/docs/basic_facade_builder/support_format.md @@ -0,0 +1,34 @@ +# `basic_facade_builder::support_format`
`basic_facade_builder::support_wformat` + +```cpp +using support_format = basic_facade_builder; + +using support_wformat = basic_facade_builder; +``` + +The member types `support_format` and `support_wformat` of `basic_facade_builder` add necessary convention and reflection types to the template parameters, enabling specializations of [`std::formatter, CharT>`](../formatter_proxy_indirect_accessor.md) where `F` is a [facade](../facade.md) type built from `basic_facade_builder`, `CharT` is `char` (if `support_format` is specified) or `wchar_t` (if `support_wformat` is specified). + +`support_format` and `support_wformat` also add constraints to a facade type `F` built from `basic_facade_builder`, requiring a contained value of `proxy` be *formattable*. Formally, let `p` be a contained value of `proxy`, `CharT` be `char` (if `support_format` is specified) or `wchar_t` (if `support_wformat` is specified), `T` be `std::decay_t`, `std::formatter` shall be an enabled specialization of `std::formatter`. + +## Example + +```cpp +#include +#include + +#include "proxy.h" + +struct Formattable : pro::facade_builder + ::support_format + ::build {}; + +int main() { + pro::proxy p = pro::make_proxy(123); + std::cout << std::format("{}", *p) << "\n"; // Prints: "123" + std::cout << std::format("{:*<6}", *p) << "\n"; // Prints: "123***" +} +``` + +## See Also + +- [class template `std::formatter`](../formatter_proxy_indirect_accessor.md) diff --git a/docs/basic_facade_builder/support_rtti.md b/docs/basic_facade_builder/support_rtti.md new file mode 100644 index 0000000..02d29b2 --- /dev/null +++ b/docs/basic_facade_builder/support_rtti.md @@ -0,0 +1,44 @@ +# `basic_facade_builder::support_rtti`
`basic_facade_builder::support_indirect_rtti`
`basic_facade_builder::support_direct_rtti` + +```cpp +using support_rtti = support_indirect_rtti; + +using support_indirect_rtti = basic_facade_builder; + +using support_direct_rtti = basic_facade_builder; +``` + +The member types `support_rtti`, `support_indirect_rtti` and `support_direct_rtti` add necessary convention and reflection types to the template parameters, enabling [RTTI](https://en.wikipedia.org/wiki/Run-time_type_information) support for [`proxy`](../proxy.md), where `F` is a [facade](../facade.md) type built from `basic_facade_builder`. For an RTTI-enabled facade `F`, non-member functions `proxy_typeid` (similar to [`std::any::type`](https://en.cppreference.com/w/cpp/utility/any/type)) and `proxy_cast` (similar to [`std::any_cast`](https://en.cppreference.com/w/cpp/utility/any/any_cast)) are available for [`proxy_indirect_accessor`](../proxy_indirect_accessor.md) (if `support_rtti` or `support_indirect_rtti` is specified) or [`proxy`](../proxy.md) (if `support_direct_rtti` is specified). + +## Non-Member Functions + +| Name | Description | +| ---------------------------------------------- | ------------------------------------------ | +| [`proxy_typeid`](support_rtti/proxy_typeid.md) | returns the `typeid` of the contained type | +| [`proxy_cast`](support_rtti/proxy_cast.md) | type-safe access to the contained object | + +## Example + +```cpp +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::support_direct_rtti + ::build {}; + +int main() { + int v = 123; + pro::proxy p = &v; + std::cout << proxy_typeid(p).name() << "\n"; // Prints: "Pi" (assuming GCC) + std::cout << proxy_cast(p) << "\n"; // Prints the address of v + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "i" (assuming GCC) + std::cout << proxy_cast(*p) << "\n"; // Prints: "123" +} +``` + +## See Also + +- [`support_view`](support_view.md) diff --git a/docs/basic_facade_builder/support_rtti/proxy_cast.md b/docs/basic_facade_builder/support_rtti/proxy_cast.md new file mode 100644 index 0000000..2c7804e --- /dev/null +++ b/docs/basic_facade_builder/support_rtti/proxy_cast.md @@ -0,0 +1,97 @@ +# Function template `proxy_cast` + +```cpp +// (1) +template +T proxy_cast(const proxy_indirect_accessor& operand); + +// (2) +template +T proxy_cast(proxy_indirect_accessor& operand); + +// (3) +template +T proxy_cast(proxy_indirect_accessor&& operand); + +// (4) +template +T proxy_cast(const proxy& operand); + +// (5) +template +T proxy_cast(proxy& operand); + +// (6) +template +T proxy_cast(proxy&& operand); + +// (7) +template +const T* proxy_cast(const proxy_indirect_accessor* operand) noexcept; + +// (8) +template +T* proxy_cast(proxy_indirect_accessor* operand) noexcept; + +// (9) +template +const T* proxy_cast(const proxy* operand) noexcept; + +// (10) +template +T* proxy_cast(proxy* operand) noexcept; +``` + +Performs type-safe access to the contained object of `proxy` where `F` is a [facade](../../facade.md) type built from `basic_facade_builder`. + +- `(1-3)` Let `p` be [`access_proxy`](../../access_proxy.md)`(operand)`, `ptr` be the contained value of `p` (if any). If `p` does not contain a value or `std::is_same_v, std::decay_t>` is `false`, throws [`bad_proxy_cast`](../../bad_proxy_cast.md). Otherwise, returns `static_cast(expr)`. Specifically, `expr` is defined as + - `(1)`: `*std::as_const(ptr)`. + - `(2)`: `*ptr`. + - `(3)`: `*std::move(ptr)`. +- `(4-6)` Let `ptr` be the contained value of `operand` (if any). If `operand` does not contain a value or `std::is_same_v, std::decay_t>` is `false`, throws [`bad_proxy_cast`](../../bad_proxy_cast.md). Otherwise, returns `static_cast(expr)`. Specifically, `expr` is defined as + - `(6)`: `std::as_const(ptr)`. + - `(7)`: `ptr`. + - `(8)`: `std::move(ptr)`. +- `(7-10)` Returns `&proxy_cast(*operand)` if the evaluation won't throw, or otherwise, returns `nullptr`. + +These functions are not visible to ordinary [unqualified](https://en.cppreference.com/w/cpp/language/unqualified_lookup) or [qualified lookup](https://en.cppreference.com/w/cpp/language/qualified_lookup). It can only be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `proxy_indirect_accessor` (if `support_rtti` or `support_indirect_rtti` is specified) or `proxy` (if `support_direct_rtti` is specified) is an associated class of the arguments. Usage of these functions is similar to [`std::any_cast`](https://en.cppreference.com/w/cpp/utility/any/any_cast). + +## Example + +```cpp +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::build {}; + +int main() { + int v = 123; + pro::proxy p; + try { + proxy_cast(*p); // Throws + } catch (const pro::bad_proxy_cast& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } + p = &v; + std::cout << proxy_cast(*p) << "\n"; // Prints: "123" + proxy_cast(*p) = 456; + std::cout << v << "\n"; // Prints: "456" + try { + proxy_cast(*p); // Throws + } catch (const pro::bad_proxy_cast& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } + int* ptr1 = proxy_cast(&*p); + std::cout << std::boolalpha << ptr1 << "\n"; // Prints an address + std::cout << std::boolalpha << &v << "\n"; // Prints the same address as above + double* ptr2 = proxy_cast(&*p); + std::cout << ptr2 << "\n"; // Prints "0" +} +``` + +## See Also + +- [function `proxy_typeid`](proxy_typeid.md) diff --git a/docs/basic_facade_builder/support_rtti/proxy_typeid.md b/docs/basic_facade_builder/support_rtti/proxy_typeid.md new file mode 100644 index 0000000..908cf07 --- /dev/null +++ b/docs/basic_facade_builder/support_rtti/proxy_typeid.md @@ -0,0 +1,39 @@ +# Function `proxy_typeid` + +```cpp +// (1) +const std::type_info& proxy_typeid(const proxy_indirect_accessor& operand) noexcept; + +// (2) +const std::type_info& proxy_typeid(const proxy& operand) noexcept; +``` + +Returns the `typeid` of the contained type of `proxy` where `F` is a [facade](../../facade.md) type built from `basic_facade_builder`. + +- `(1)` Let `p` be [`access_proxy`](../../access_proxy.md)`(operand)`, `ptr` be the contained value of `p` (if any). Returns `typeid(std::decay_t)` if `p` contains a value, or otherwise, `typeid(void)`. +- `(2)` Let `ptr` be the contained value of `operand` (if any). Returns `typeid(std::decay_t)` if `operand` contains a value `ptr`, or otherwise, `typeid(void)`. + +These functions are not visible to ordinary [unqualified](https://en.cppreference.com/w/cpp/language/unqualified_lookup) or [qualified lookup](https://en.cppreference.com/w/cpp/language/qualified_lookup). It can only be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `proxy_indirect_accessor` (if `support_rtti` or `support_indirect_rtti` is specified) or `proxy` (if `support_direct_rtti` is specified) is an associated class of the arguments. + +## Example + +```cpp +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::build {}; + +int main() { + pro::proxy p; + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "v" (assuming GCC) + p = pro::make_proxy(123); + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "i" (assuming GCC) +} +``` + +## See Also + +- [function template `proxy_cast`](proxy_cast.md) diff --git a/docs/basic_facade_builder/support_view.md b/docs/basic_facade_builder/support_view.md new file mode 100644 index 0000000..810068f --- /dev/null +++ b/docs/basic_facade_builder/support_view.md @@ -0,0 +1,45 @@ +# `basic_facade_builder::add_view` + +```cpp +template +using add_view = basic_facade_builder; +``` + +The alias template `add_view` of `basic_facade_builder` adds necessary convention types to allow implicit conversion from [`proxy`](../proxy.md)`` to [`proxy_view`](../observer_facade.md)`` where `F1` is a [facade](../facade.md) type built from `basic_facade_builder`. + +Let `p` be a value of type `proxy`, `ptr` be the contained value of `p` (if any). + +- When `F` is not `const`-qualified, the conversion from type `proxy&` to type `proxy_view` is equivalent to `return std::addressof(*ptr)` if `p` contains a value, or otherwise equivalent to `return nullptr`. +- When `F` is `const`-qualified, the conversion from type `const proxy&` to type `proxy_view` is equivalent to `return std::addressof(*std::as_const(ptr))` if `p` contains a value, or otherwise equivalent to `return nullptr`. + +## Notes + +`add_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 + +```cpp +#include +#include +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::add_view + ::add_view + ::build {}; + +int main() { + pro::proxy p = pro::make_proxy(123); + pro::proxy_view pv = p; + pro::proxy_view pcv = p; + proxy_cast(*pv) = 456; // Modifies the contained object of p + std::cout << proxy_cast(*pcv) << "\n"; // Prints: "456" +} +``` + +## See Also + +- [`add_convention`](add_convention.md) diff --git a/docs/explicit_conversion_dispatch.md b/docs/explicit_conversion_dispatch.md index 929ceb3..2234250 100644 --- a/docs/explicit_conversion_dispatch.md +++ b/docs/explicit_conversion_dispatch.md @@ -1,9 +1,9 @@ # Class `explicit_conversion_dispatch` ```cpp -class explicit_conversion_dispatch; +class explicit_conversion_dispatch; // since 3.2 -using conversion_dispatch = explicit_conversion_dispatch; +using conversion_dispatch = explicit_conversion_dispatch; // since 3.2 ``` Class `explicit_conversion_dispatch` models a [dispatch](ProDispatch.md) type for explicit type conversion expressions. It meets the [*ProAccessible* requirements](ProAccessible.md) of applicable types. `conversion_dispatch` is an alias of `explicit_conversion_dispatch`. diff --git a/docs/explicit_conversion_dispatch/operator_call.md b/docs/explicit_conversion_dispatch/operator_call.md index be8de4c..08e14cb 100644 --- a/docs/explicit_conversion_dispatch/operator_call.md +++ b/docs/explicit_conversion_dispatch/operator_call.md @@ -2,7 +2,7 @@ ```cpp template -/* see below */ operator()(T&& value) noexcept; +/* see below */ operator()(T&& value) const noexcept; ``` Returns a value that is implicitly convertible to any type `U` with expression `U{std::forward(value)}` when `T` is explicitly convertible to type `U`. diff --git a/docs/faq.md b/docs/faq.md index 837a7e1..6431579 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -31,25 +31,24 @@ While virtual functions have served well for decades, "Proxy" offers modern solu ### How to learn "Proxy" effectively? -The fundamental abstraction of "Proxy" is called "facade". It is recommended for beginners to start with the examples in the [README](../README.md), try to understand the pattern of defining a [`facade`](ProFacade.md) type, using a facade type to specify a [`proxy`](proxy.md) type, and creating and using a proxy object at runtime. Don't hesitate to consult the [specifications](specifications.md) for more details about any facility in the library. +The fundamental abstraction of "Proxy" is called "facade". It is recommended for beginners to start with the examples in the [README](../README.md), try to understand the pattern of defining a [`facade`](facade.md) type, using a facade type to specify a [`proxy`](proxy.md) type, and creating and using a proxy object at runtime. Don't hesitate to consult the [specifications](specifications.md) for more details about any facility in the library. ### How to integrate "Proxy" into my project? Since "Proxy" is a single-header library, you can simply navigate to the [latest release](https://github.com/microsoft/proxy/releases), download the source code, and include "proxy.h" in your project. Make sure your compiler version meets the [minimum requirements for compilers](../README.md#compiler-req). If your project has already integrated with [vcpkg](https://vcpkg.io/) or [conan](https://conan.io/), just search for the keyword "proxy" and install it. Thanks to the community that helped port "Proxy" to these platforms! - ### My existing project uses virtual functions. How should I migrate to "Proxy"? Follow the 4 steps below to upgrade an existing project from using virtual functions to "Proxy": 1. Update the compiler version to meet our [minimum requirements for compilers](../README.md#compiler-req). -2. Define [`facade`](ProFacade.md) types that match the "base classes with virtual functions" (virtual base classes). +2. Define [`facade`](facade.md) types that match the "base classes with virtual functions" (virtual base classes). 3. Replace all the usage of virtual base classes with [`proxy`](proxy.md) from the API boundary. 4. Remove all the definitions and inheritance of virtual base classes. ### How is the performance compared to virtual functions? -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, when a construct of runtime abstraction has ownership of its context, "Proxy" usually has better performance than the inheritance-based approach in lifetime management. When performing an indirect call, "Proxy" usually generates similar code to a virtual function with equal performance. We have observed that when the concrete implementation and the abstraction appear in the same translation unit, a virtual function is more likely to be devirtualized in the generated code, but sometimes the compiler won't do that trick for "Proxy". +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. ### Why is "Proxy" based on pointer semantics rather than value semantics like [std::function](https://en.cppreference.com/w/cpp/utility/functional/function)? @@ -57,7 +56,7 @@ At the beginning, we explored the feasibility of designing a general-purpose pol ### Why does "Proxy" define several macros instead of modern C++ facilities? -"Proxy" defines 4 macros: [`__msft_lib_proxy`](msft_lib_proxy.md), [`PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md), [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md), and [`PRO_DEF_WEAK_DISPATCH`](PRO_DEF_WEAK_DISPATCH.md). [`__msft_lib_proxy`](msft_lib_proxy.md) is the feature test macro, following the existing practice in the C++20 standard. The other 3 macros are fundamental facilities to define a custom [`dispatch`](ProDispatch.md) type. These macros cannot be replaced by modern C++ facilities because there is no existing language feature prior to C++26 that allows generating a function with an arbitrary name. As a result, "Proxy" does not provide a default interface for [modules](https://en.cppreference.com/w/cpp/language/modules) as of now. +"Proxy" defines 4 macros: [`__msft_lib_proxy`](msft_lib_proxy.md), [`PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md), [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md), and [`PRO_DEF_FREE_AS_MEM_DISPATCH`](PRO_DEF_FREE_AS_MEM_DISPATCH.md). [`__msft_lib_proxy`](msft_lib_proxy.md) is the feature test macro, following the existing practice in the C++20 standard. The other 3 macros are fundamental facilities to define a custom [`dispatch`](ProDispatch.md) type. These macros cannot be replaced by modern C++ facilities because there is no existing language feature prior to C++26 that allows generating a function with an arbitrary name. As a result, "Proxy" does not provide a default interface for [modules](https://en.cppreference.com/w/cpp/language/modules) as of now. ### What is the standardization progress of this library? diff --git a/docs/formatter_proxy_indirect_accessor.md b/docs/formatter_proxy_indirect_accessor.md new file mode 100644 index 0000000..f6b524a --- /dev/null +++ b/docs/formatter_proxy_indirect_accessor.md @@ -0,0 +1,24 @@ +# `std::formatter` + +```cpp +namespace std { + template requires(/* see below */) + struct formatter, ChatT>; +} +``` + +The template specialization of [`std::formatter`](https://en.cppreference.com/w/cpp/utility/format/formatter) for [`proxy_indirect_accessor`](proxy_indirect_accessor.md) allows users to convert an object managed by [`proxy`](proxy.md) to string using [formatting functions](https://en.cppreference.com/w/cpp/utility/format) such as [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format). The specialization is enabled when `F` is built from [`basic_facade_builder`](basic_facade_builder.md) and + +- when `CharT` is `char`, specified [`support_format`](basic_facade_builder/support_format.md), or +- when `CharT` is `wchar_t`, specified [`support_wformat`](basic_facade_builder/support_format.md). + +## Member Functions + +| Name | Description | +| ------------------------------------------------------------ | ----------------------------------- | +| [`parse`](formatter_proxy_indirect_accessor/parse.md) [constexpr] | parse the format spec | +| [`format`](formatter_proxy_indirect_accessor/format.md) | format according to the format spec | + +## See Also + +- [`basic_facade_builder::support_format`](basic_facade_builder/support_format) diff --git a/docs/formatter_proxy_indirect_accessor/format.md b/docs/formatter_proxy_indirect_accessor/format.md new file mode 100644 index 0000000..042c2f5 --- /dev/null +++ b/docs/formatter_proxy_indirect_accessor/format.md @@ -0,0 +1,8 @@ +# `std::formatter::format()` + +```cpp +template +OutIt format(const pro::proxy_indirect_accessor& ia, basic_format_context& fc) const; +``` + +Formats the managed object `o` represented by `ia` with `std::formatter, CharT>` according to the specifiers stored in `*this`, writes the output to `fc.out()` and returns an end iterator of the output range. diff --git a/docs/formatter_proxy_indirect_accessor/parse.md b/docs/formatter_proxy_indirect_accessor/parse.md new file mode 100644 index 0000000..fcf87d0 --- /dev/null +++ b/docs/formatter_proxy_indirect_accessor/parse.md @@ -0,0 +1,7 @@ +# `std::formatter::parse()` + +```cpp +constexpr typename basic_format_parse_context::iterator parse(basic_format_parse_context& pc); +``` + +In the range [`pc.begin()`, `pc.end()`), parses and stores the *format-spec* until the first `}` character. diff --git a/docs/implicit_conversion_dispatch.md b/docs/implicit_conversion_dispatch.md index 38fdac9..6620be4 100644 --- a/docs/implicit_conversion_dispatch.md +++ b/docs/implicit_conversion_dispatch.md @@ -1,7 +1,7 @@ # Class `implicit_conversion_dispatch` ```cpp -class implicit_conversion_dispatch; +class implicit_conversion_dispatch; // since 3.2 ``` Class `implicit_conversion_dispatch` models a [dispatch](ProDispatch.md) type for implicit type conversion expressions. It meets the [*ProAccessible* requirements](ProAccessible.md) of applicable types. diff --git a/docs/implicit_conversion_dispatch/operator_call.md b/docs/implicit_conversion_dispatch/operator_call.md index 7cd8ee3..55eb2bf 100644 --- a/docs/implicit_conversion_dispatch/operator_call.md +++ b/docs/implicit_conversion_dispatch/operator_call.md @@ -2,7 +2,7 @@ ```cpp template -T&& operator()(T&& value) noexcept; +T&& operator()(T&& value) const noexcept; ``` Returns `std::forward(value)`. diff --git a/docs/msft_lib_proxy.md b/docs/msft_lib_proxy.md index f9c36a6..77d85e2 100644 --- a/docs/msft_lib_proxy.md +++ b/docs/msft_lib_proxy.md @@ -8,6 +8,7 @@ Similar to the standard [feature test macros](https://en.cppreference.com/w/cpp/ | Version | Value of `__msft_lib_proxy` | | ------- | --------------------------- | +| 3.2.0 | `202501L` | | 3.1.0 | `202410L` | | 3.0.0 | `202408L` | diff --git a/docs/not_implemented.md b/docs/not_implemented.md new file mode 100644 index 0000000..d5002cb --- /dev/null +++ b/docs/not_implemented.md @@ -0,0 +1,15 @@ +# Class `not_implemented` + +```cpp +class not_implemented : public std::exception; +``` + +A type of object to be thrown by the default implementation of [`weak_dispatch`](weak_dispatch.md). + +## Member Functions + +| Name | Description | +| ------------- | ------------------------------------- | +| (constructor) | constructs a `not_implemented` object | +| (destructor) | destroys a `not_implemented` object | +| `what` | returns the explanatory string | diff --git a/docs/observer_facade.md b/docs/observer_facade.md new file mode 100644 index 0000000..3e55e17 --- /dev/null +++ b/docs/observer_facade.md @@ -0,0 +1,57 @@ +# Class template `observer_facade`
Alias template `proxy_view` + +```cpp +template +struct observer_facade; + +template +using proxy_view = proxy>; +``` + +Class template `observer_facade` is a [facade](facade.md) type for raw pointers potentially dereferenced from a `proxy` object. To instantiate `observer_facade`, `F` shall model [concept `facade`](facade.md) and optionally `const`-qualified. + +## Member Types of `observer_facade` + +| Name | Description | +| ------------------ | ------------------------------------------------------------ | +| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::convention_types`. Specifically, 1) For each convention type `C` defined in `typename F::convention_types`, `C` is reserved when `C::is_direct` is `false`, or otherwise filtered out. 2) For each overload type `O` defined in `typename C::overload_types` of each type `C`, `O` is reserved and transformed into the `const`-qualified type (e.g., `void(int)` is transformed into `void(int) const`) when `O` is not rvalue-reference-qualified and matches the `const`-ness of `F` except when `C` is an upward conversion convention added via [`basic_facade_builder::add_facade`](basic_facade_builder/add_facade.md), or otherwise filtered out. 3) Each type `C` is reserved when `std::tuple_size_v` is greater than `0`, or otherwise filtered out. | +| `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 reserved when `R::is_direct` is `false`, or otherwise filtered out. | + +## Member Constants of `observer_facade` + +| Name | Description | +| ---------------------------------- | ------------------------------------------------------------ | +| `constraints` [static] [constexpr] | A value of type `proxiable_ptr_constraints` indicating constraints for a raw pointer type. | + +## Example + +```cpp +#include + +#include "proxy.h" + +PRO_DEF_MEM_DISPATCH(MemAt, at); + +template +struct FMap : pro::facade_builder + ::add_convention + ::build {}; + +int main() { + std::map v{{1, 2}}; + + pro::proxy_view> p1 = &v; + static_assert(std::is_same_vat(1)), int&>); + p1->at(1) = 3; + printf("%d\n", v.at(1)); // Prints "3" + + pro::proxy_view> p2 = &std::as_const(v); + static_assert(std::is_same_vat(1)), const int&>); + // p2->at(1) = 4; won't compile + printf("%d\n", p2->at(1)); // Prints "3" +} +``` + +## See also + +[`basic_facade_builder::support_view`](basic_facade_builder/support_view.md) diff --git a/docs/operator_dispatch.md b/docs/operator_dispatch.md index 758708b..52114d4 100644 --- a/docs/operator_dispatch.md +++ b/docs/operator_dispatch.md @@ -25,7 +25,7 @@ The following 8 SOPs are not supported: ## Type Conversion -Type conversion expressions, although using `operator` syntax, do not have a specific sign and are not within the scope of `operator_dispatch`. They are supported by another class template [`conversion_dispatch`](conversion_dispatch.md). +Type conversion expressions, although using `operator` syntax, do not have a specific sign and are not within the scope of `operator_dispatch`. They are supported by [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](explicit_conversion_dispatch.md) and [class `implicit_conversion_dispatch`](implicit_conversion_dispatch.md). ## Specializations @@ -140,4 +140,4 @@ int main() { ## See Also -- [class template `conversion_dispatch`](conversion_dispatch.md) +- [class `conversion_dispatch`](explicit_conversion_dispatch.md) diff --git a/docs/operator_dispatch/operator_call.md b/docs/operator_dispatch/operator_call.md index 37e646a..87ba41e 100644 --- a/docs/operator_dispatch/operator_call.md +++ b/docs/operator_dispatch/operator_call.md @@ -13,13 +13,13 @@ When `Sign` is one of `"+"`, `"-"`, `"*"`, `"&"`, ```cpp // (1) template -/* see below */ operator()(T&& self) +/* see below */ operator()(T&& self) const noexcept(noexcept(sop std::forward(self))) requires(requires { sop std::forward(self); }); // (2) template -/* see below */ operator()(T&& self, Arg&& arg) +/* see below */ operator()(T&& self, Arg&& arg) const noexcept(noexcept(std::forward(self) sop std::forward(arg))) requires(requires { std::forward(self) sop std::forward(arg); }); ``` @@ -35,7 +35,7 @@ When `Sign` is one of `"/"`, `"%"`, `"=="`, `"!="`, `">"`, `"<"`, `">="`, `"<="` ```cpp // (3) template -/* see below */ operator()(T&& self, Arg&& arg) +/* see below */ operator()(T&& self, Arg&& arg) const noexcept(noexcept(std::forward(self) sop std::forward(arg))) requires(requires { std::forward(self) sop std::forward(arg); }); ``` @@ -49,13 +49,13 @@ When `Sign` is either `"++"`, `"--"`, ```cpp // (4) template -/* see below */ operator()(T&& self) +/* see below */ operator()(T&& self) const noexcept(noexcept(sop std::forward(self))) requires(requires { sop std::forward(self); }); // (5) template -/* see below */ operator()(T&& self, int) +/* see below */ operator()(T&& self, int) const noexcept(noexcept(std::forward(self) sop)) requires(requires { std::forward(self) sop; }); ``` @@ -71,7 +71,7 @@ When `Sign` is either `"!"`, `"~"`, ```cpp // (6) template -/* see below */ operator()(T&& self) +/* see below */ operator()(T&& self) const noexcept(noexcept(sop std::forward(self))) requires(requires { sop std::forward(self); }); ``` @@ -85,14 +85,13 @@ When `Sign` is `"()"`, ```cpp // (7) template -/* see below */ operator()(T&& self, Args&&... args) +/* see below */ operator()(T&& self, Args&&... args) const noexcept(noexcept(std::forward(self)(std::forward(args)...))) requires(requires { std::forward(self)(std::forward(args)...); }); ``` `(7)` Returns `std::forward(self)(std::forward(args)...)`. - ### `"[]"` When `Sign` is `"[]"`, @@ -100,13 +99,13 @@ When `Sign` is `"[]"`, ```cpp // (8) (until C++23) template -/* see below */ operator()(T&& self, Args&&... args) +/* see below */ operator()(T&& self, Args&&... args) const noexcept(noexcept(std::forward(self)[std::forward(args)...])) requires(requires { std::forward(self)[std::forward(args)...]; }); // (9) (since C++23) template -/* see below */ operator()(T&& self, Arg&& arg) +/* see below */ operator()(T&& self, Arg&& arg) const noexcept(noexcept(std::forward(self)[std::forward(arg)])) requires(requires { std::forward(self)[std::forward(arg)]; }); ``` @@ -122,7 +121,7 @@ When `Sign` is one of `"+"`, `"-"`, `"*"`, `"/"`, `"%"`, `"=="`, `"!="`, `">"`, ```cpp // (10) template -/* see below */ operator()(T&& self, Arg&& arg) +/* see below */ operator()(T&& self, Arg&& arg) const noexcept(noexcept(std::forward(arg) sop std::forward(self))) requires(requires { std::forward(arg) sop std::forward(self); }); ``` diff --git a/docs/proxy.md b/docs/proxy.md index 04c3578..a95cb12 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -5,31 +5,31 @@ template class proxy; ``` -Class template `proxy` is a general-purpose polymorphic wrapper for C++ objects. Unlike other polymorphic wrappers in the C++ standard (e.g., [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function), [`std::any`](https://en.cppreference.com/w/cpp/utility/any), etc.), `proxy` is based on pointer semantics. It supports flexible lifetime management without runtime [garbage collection (GC)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) at runtime, and offers best-in-class code generation quality, extendibility and accessibility. +Class template `proxy` is a general-purpose polymorphic wrapper for C++ objects. Unlike other polymorphic wrappers in the C++ standard (e.g., [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function), [`std::any`](https://en.cppreference.com/w/cpp/utility/any), etc.), `proxy` is based on pointer semantics. It supports flexible lifetime management without runtime [garbage collection (GC)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)), and offers best-in-class code generation quality, extendibility and accessibility. To instantiate `proxy`, `F` shall model [concept `facade`](facade.md). As per `facade`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F`, `typename T::template accessor` is inherited by `proxy` when `T::is_direct` is `true`. Otherwise, it is inherited by [`proxy_indirect_accessor`](proxy_indirect_accessor.md), the return type of [`operator*`](proxy/indirection.md), when `T::is_direct` is `false`. Implementation of accessors can call [`access_proxy`](access_proxy.md) to access the `proxy` object. It is recommended to use [`facade_builder`](basic_facade_builder.md) to define a facade type. Any instance of `proxy` at any given point in time either *contains a value* or *does not contain a value*. If a `proxy` *contains a value*, the type of the value shall be a pointer type `P` where [`proxiable`](proxiable.md) is `true`, and the value is guaranteed to be allocated as part of the `proxy` object footprint, i.e. no dynamic memory allocation occurs. However, `P` may allocate during its construction, depending on its implementation. -## Member functions +## Member Functions | Name | Description | | ---------------------------------------------------------- | -------------------------------------------------- | | [(constructor)](proxy/constructor.md) | constructs a `proxy` object | | [(destructor)](proxy/destructor.md) | destroys a `proxy` object | -| [`operator=`](proxy/assignment.md) | assigns a `proxy` object | +| [`emplace`](proxy/emplace.md) | constructs the contained value in-place | | [`operator bool`
`has_value`](proxy/operator_bool.md) | checks if the `proxy` contains a value | +| [`operator->`
`operator*`](proxy/indirection.md) | accesses the accessors of the indirect conventions | +| [`operator=`](proxy/assignment.md) | assigns a `proxy` object | | [`reset`](proxy/reset.md) | destroys any contained value | | [`swap`](proxy/swap.md) | exchanges the contents | -| [`emplace`](proxy/emplace.md) | constructs the contained value in-place | -| [`operator->`
`operator*`](proxy/indirection.md) | accesses the accessors of the indirect conventions | -## Non-member functions +## Non-Member Functions | Name | Description | | ------------------------------------------------- | ------------------------------------------------------------ | -| [`swap`](proxy/friend_swap.md) | overload the [`std::swap`](https://en.cppreference.com/w/cpp/algorithm/swap) algorithm | | [`operator==`](proxy/friend_operator_equality.md) | compares a `proxy` with `nullptr` | +| [`swap`](proxy/friend_swap.md) | overload the [`std::swap`](https://en.cppreference.com/w/cpp/algorithm/swap) algorithm | ## Comparing with Other Standard Polymorphic Wrappers diff --git a/docs/proxy/constructor.md b/docs/proxy/constructor.md index 1c33516..a3fafed 100644 --- a/docs/proxy/constructor.md +++ b/docs/proxy/constructor.md @@ -45,7 +45,7 @@ Creates a new `proxy`. - `(1)` Default constructor and the constructor taking `nullptr` construct a `proxy` that does not contain a value. - `(2)` Copy constructor constructs a `proxy` whose contained value is that of `rhs` if `rhs` contains a value, or otherwise, constructs a `proxy` that does not contain a value. As per the `requires` clause, the copy constructor is trivial when `F::constraints.copyability == constraint_level::trivial`. - `(3)` Move constructor constructs a `proxy` whose contained value is that of `rhs` if `rhs` contains a value, or otherwise, constructs a `proxy` that does not contain a value. `rhs` is in a valid but unspecified state after move construction. As per the `requires` clause, the move constructor does not participate in overload resolution when `F::constraints.copyability == constraint_level::trivial`, so that a move construction falls back to the trivial copy constructor. -- `(4)` Let `VP` be `std::decay_t

`. Constructor taking a value of pointer constructs a `proxy` whose contained value is of type `VP` and direct-non-list-initialized with `std::forward

(ptr)`. +- `(4)` Let `VP` be `std::decay_t

`. Constructor taking a value of pointer constructs a `proxy` whose contained value is of type `VP` and direct-non-list-initialized with `std::forward

(ptr)`. This overload participates in overload resolution only if `std::decay_t

` is not the same type as `proxy` nor a specialization of `std::in_place_type_t`. - `(5)` Constructs a `proxy` whose contained value is of type `P` and direct-non-list-initialized with `std::forward(args)...`. - `(6)` Constructs a `proxy` whose contained value is of type `P` and direct-non-list-initialized with `il, std::forward(args)...`. diff --git a/docs/proxy/emplace.md b/docs/proxy/emplace.md index 4150b74..971b386 100644 --- a/docs/proxy/emplace.md +++ b/docs/proxy/emplace.md @@ -71,5 +71,5 @@ int main() { ## See Also -- [(constructor)](proxy.md) +- [(constructor)](constructor.md) - [`reset`](reset.md) diff --git a/docs/proxy_indirect_accessor.md b/docs/proxy_indirect_accessor.md index 6061002..d50e60b 100644 --- a/docs/proxy_indirect_accessor.md +++ b/docs/proxy_indirect_accessor.md @@ -2,12 +2,12 @@ ```cpp template -class proxy_indirect_accessor; +class proxy_indirect_accessor; // since 3.2 ``` Class template `proxy_indirect_accessor` provides indirection accessibility for `proxy`. To instantiate `proxy_indirect_accessor`, `F` shall model [concept `facade`](facade.md). As per `facade`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F` and `T::is_direct` is `false`, `typename T::template accessor` is inherited by `proxy_indirect_accessor`. -## Member functions +## Member Functions | Name | Description | | ----------------------- | ----------------------------------------- | diff --git a/docs/specifications.md b/docs/specifications.md index 46c7ab5..72bbd8e 100644 --- a/docs/specifications.md +++ b/docs/specifications.md @@ -7,53 +7,58 @@ This document provides the API specifications for the C++ library Proxy (version | Name | Description | | --------------------------------------------------------- | ------------------------------------------------------------ | | [`facade`](facade.md) | Specifies that a type models a "facade" | -| [`proxiable`](proxiable.md) | Specifies that a pointer type can instantiate a `proxy` | | [`inplace_proxiable_target`](inplace_proxiable_target.md) | Specifies that a value type can instantiate a `proxy` without allocation | +| [`proxiable`](proxiable.md) | Specifies that a pointer type can instantiate a `proxy` | ## Classes | Name | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| [`proxy`](proxy.md) | Wraps a pointer object matching specified facade | +| [`bad_proxy_cast`](bad_proxy_cast.md)
*(since 3.2)* | Exception thrown by the value-returning forms of `proxy_cast` on a type mismatch | | [`basic_facade_builder`
`facade_builder`](basic_facade_builder.md) | Provides capability to build a facade type at compile-time | | [`constraint_level`](constraint_level.md) | Defines the 4 constraint levels of a special member function | -| [`proxiable_ptr_constraints`](proxiable_ptr_constraints.md) | Defines the constraints of a pointer type to instantiate a `proxy` | +| [`explicit_conversion_dispatch`
`conversion_dispatch`](explicit_conversion_dispatch.md)
*(since 3.2)* | Dispatch type for explicit conversion expressions with accessibility | +| [`implicit_conversion_dispatch`](implicit_conversion_dispatch.md)
*(since 3.2)* | Dispatch type for implicit conversion expressions with accessibility | +| [`not_implemented` ](not_implemented.md)
*(since 3.2)* | Exception thrown by `weak_dispatch` for the default implementation | +| [`observer_facade`
`proxy_view`](observer_facade.md)
*(since 3.2)* | Non-owning proxy optimized for raw pointers | | [`operator_dispatch`](operator_dispatch.md) | Dispatch type for operator expressions with accessibility | -| [`explicit_conversion_dispatch`
`conversion_dispatch`](explicit_conversion_dispatch.md) | Dispatch type for explicit conversion expressions with accessibility | -| [`implicit_conversion_dispatch`](implicit_conversion_dispatch.md) | Dispatch type for implicit conversion expressions with accessibility | -| [`proxy_indirect_accessor`](proxy_indirect_accessor.md) | Provides indirection accessibility for `proxy` | +| [`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)
*(since 3.2)* | Provides indirection accessibility for `proxy` | +| [`proxy`](proxy.md) | Wraps a pointer object matching specified facade | +| [`std::formatter`](formatter_proxy_indirect_accessor.md)
*(since 3.2)* | Formatting support for `proxy_indirect_accessor` | +| [`weak_dispatch`](weak_dispatch.md)
*(since 3.2)* | Weak dispatch type with a default implementation that throws `not_implemented` | ## Functions | Name | Description | | --------------------------------------------- | ------------------------------------------------------------ | -| [`make_proxy`](make_proxy.md) | Creates a `proxy` object potentially with heap allocation | -| [`make_proxy_inplace`](make_proxy_inplace.md) | Creates a `proxy` object with strong no-allocation guarantee | +| [`access_proxy`](access_proxy.md) | Accesses a `proxy` object via an accessor | | [`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 | +| [`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 | -| [`access_proxy`](access_proxy.md) | Accesses a `proxy` object via an accessor | ## Macros | Name | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| [`PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md) | Defines a dispatch type for member function call expressions with accessibility | -| [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md) | Defines a dispatch type for free function call expressions with accessibility | -| [`PRO_DEF_FREE_AS_MEM_DISPATCH`](PRO_DEF_FREE_AS_MEM_DISPATCH.md) | Defines a dispatch type for free function call expressions with accessibility via a member function | -| [`PRO_DEF_WEAK_DISPATCH`](PRO_DEF_WEAK_DISPATCH.md) | Defines a weak dispatch type with a default implementation | | [`__msft_lib_proxy`](msft_lib_proxy.md) | Feature test macro | +| [`PRO_DEF_FREE_AS_MEM_DISPATCH` ](PRO_DEF_FREE_AS_MEM_DISPATCH.md)
*(since 3.1)* | Defines a dispatch type for free function call expressions with accessibility via a member function | +| [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md) | Defines a dispatch type for free function call expressions with accessibility | +| [`PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md) | Defines a dispatch type for member function call expressions with accessibility | +| [`PRO_DEF_WEAK_DISPATCH`](PRO_DEF_WEAK_DISPATCH.md)
*(deprecated since 3.2)* | Defines a weak dispatch type with a default implementation | ## Named Requirements | Name | Description | | --------------------------------------------- | ------------------------------------------------------------ | -| [*ProBasicFacade*](ProBasicFacade.md) | Specifies that a type potentially models a "facade" of `proxy` | +| [*ProAccessible*](ProAccessible.md) | Specifies that a type provides accessibility to `proxy` | | [*ProBasicConvention*](ProBasicConvention.md) | Specifies that a type potentially models a "convention" | +| [*ProBasicFacade*](ProBasicFacade.md) | Specifies that a type potentially models a "facade" of `proxy` | | [*ProBasicReflection*](ProBasicReflection.md) | Specifies that a type potentially models a "reflection" | -| [*ProFacade*](ProFacade.md) | Specifies that a type models a "facade" of `proxy` | | [*ProConvention*](ProConvention.md) | Specifies that a type models a "convention" | -| [*ProReflection*](ProReflection.md) | Specifies that a type models a "reflection" | | [*ProDispatch*](ProDispatch.md) | Specifies that a type models a "dispatch" | +| [*ProFacade*](ProFacade.md) | Specifies that a type models a "facade" of `proxy` | | [*ProOverload*](ProOverload.md) | Specifies that a type models an "overload" | -| [*ProAccessible*](ProAccessible.md) | Specifies that a type provides accessibility to `proxy` | +| [*ProReflection*](ProReflection.md) | Specifies that a type models a "reflection" | diff --git a/docs/weak_dispatch.md b/docs/weak_dispatch.md new file mode 100644 index 0000000..25cbe75 --- /dev/null +++ b/docs/weak_dispatch.md @@ -0,0 +1,54 @@ +# Class template `weak_dispatch` + +```cpp +template +struct weak_dispatch : D; +``` + +Class template `weak_dispatch` extends an existing [dispatch](ProDispatch.md) type `D` and offers a default implementation of `operator()` that throws [`not_implemented`](not_implemented.md) when a convention is not implemented in `D`. + +## Member functions + +| Name | Description | +| ---------------------------------------------- | ----------------------------------- | +| (constructor) | constructs a `weak_dispatch` object | +| (destructor) | destroys a `weak_dispatch` object | +| [`operator()`](weak_dispatch/operator_call.md) | invokes the dispatch | + +## Notes + +A "weak dispatch" can extend an existing dispatch with a default implementation that does not depend on the contained value of a `proxy` object. This is useful when instantiating a `proxy` with a value that does not support some conventions defined by `F`. Compared to wrapping the default implementation with [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md), using "weak dispatch" when applicable can effectively improve compilation speed and binary size, in case some contained value of a `proxy` object does not participate code generation. + +In [Java](https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html#jls-9.4-200) or [C#](https://learn.microsoft.com/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods), a "default method" can invoke other abstract methods defined in a same `interface`. This pattern is discouraged when using the Proxy library because the invocations are not necessarily indirect. If a "default implementation" otherwise needs to observe the contained value of a `proxy` object, it is encouraged to define a separate free function, and subsequently define a dispatch type of it by using [`PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md) or [`PRO_DEF_FREE_AS_MEM_DISPATCH`](PRO_DEF_FREE_AS_MEM_DISPATCH.md). + +## Example + +```cpp +#include +#include +#include + +#include "proxy.h" + +PRO_DEF_MEM_DISPATCH(MemAt, at); + +struct WeakDictionary : pro::facade_builder + ::add_convention, std::string(int index) const> + ::build {}; + +int main() { + std::vector v{"hello", "world"}; + pro::proxy p1 = &v; + std::cout << p1->at(1) << "\n"; // Prints: "world" + pro::proxy p2 = pro::make_proxy(123); + try { + p2->at(1); + } catch (const pro::not_implemented& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } +} +``` + +## See Also + +- [named requirements *ProDispatch*](ProDispatch.md) diff --git a/docs/weak_dispatch/operator_call.md b/docs/weak_dispatch/operator_call.md new file mode 100644 index 0000000..062a82b --- /dev/null +++ b/docs/weak_dispatch/operator_call.md @@ -0,0 +1,13 @@ +# `weak_dispatch::operator()` + +```cpp +// (1) +using D::operator(); + +// (2) +template +[[noreturn]] /* see below */ operator()(std::nullptr_t, Args&&...) const; +``` + +- `(1)` Forwards an invocation to `D` if applicable. +- `(2)` Throws [`not_implemented`](../not_implemented.md). The return type is convertible to any type to match a arbitrary convention. diff --git a/proxy.h b/proxy.h index b2189cc..aeb76ed 100644 --- a/proxy.h +++ b/proxy.h @@ -52,7 +52,7 @@ #define ___PRO_DEBUG(...) __VA_ARGS__ #endif // NDEBUG -#define __msft_lib_proxy 202410L +#define __msft_lib_proxy 202501L namespace pro { @@ -222,8 +222,7 @@ R indirect_conv_dispatcher(add_qualifier_t self, Args... args) template R direct_conv_dispatcher(add_qualifier_t self, Args... args) noexcept(invocable_dispatch_ptr_direct) { - auto& qp = *std::launder( - reinterpret_cast>(&self)); + auto& qp = *std::launder(reinterpret_cast>(&self)); if constexpr (Q == qualifier_type::rv) { destruction_guard guard{&qp}; return invoke_dispatch( @@ -722,10 +721,9 @@ struct proxy_helper { #if defined(__NVCOMPILER) #pragma diagnostic pop #endif // defined(__NVCOMPILER) - return reinterpret_cast, Q>>( - *(reinterpret_cast>( - static_cast, Q>>( - std::addressof(a))) - offset)); + return reinterpret_cast, Q>>(*(reinterpret_cast< + add_qualifier_ptr_t>(static_cast, Q>>(std::addressof(a))) - offset)); } } }; @@ -872,9 +870,8 @@ class proxy : public details::facade_traits::direct_accessor { ~proxy() noexcept(F::constraints.destructibility == constraint_level::nothrow) requires(F::constraints.destructibility == constraint_level::nontrivial || F::constraints.destructibility == constraint_level::nothrow) { - if (meta_.has_value()) { - meta_->_Traits::destructibility_meta::dispatcher(*ptr_); - } + if (meta_.has_value()) + { meta_->_Traits::destructibility_meta::dispatcher(*ptr_); } } bool has_value() const noexcept { return meta_.has_value(); } @@ -930,7 +927,7 @@ class proxy : public details::facade_traits::direct_accessor { proxy_indirect_accessor&& operator*() && noexcept { return std::move(ia_); } const proxy_indirect_accessor&& operator*() const&& noexcept - { return std::forward>(ia_); } + { return std::move(ia_); } friend void swap(proxy& lhs, proxy& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } @@ -942,8 +939,7 @@ class proxy : public details::facade_traits::direct_accessor { P& initialize(Args&&... args) { P& result = *std::construct_at( reinterpret_cast(ptr_), std::forward(args)...); - if constexpr (requires { (bool)result; }) - { assert((bool)result); } + if constexpr (std::is_constructible_v) { assert(result); } meta_ = details::meta_ptr{std::in_place_type

}; return result; } @@ -1171,8 +1167,7 @@ template proxy allocate_proxy(const Alloc& alloc, Args&&... args) { return details::allocate_proxy_impl(alloc, std::forward(args)...); } -template +template proxy allocate_proxy(const Alloc& alloc, std::initializer_list il, Args&&... args) { return details::allocate_proxy_impl( @@ -1218,18 +1213,17 @@ using proxy_view = proxy>; __MACRO(noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ __MACRO(&, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ __MACRO(& noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ - __MACRO(&&, ::pro::access_proxy<__F>(::std::forward(*this)), \ + __MACRO(&&, ::pro::access_proxy<__F>(::std::move(*this)), __VA_ARGS__); \ + __MACRO(&& noexcept, ::pro::access_proxy<__F>(::std::move(*this)), \ __VA_ARGS__); \ - __MACRO(&& noexcept, ::pro::access_proxy<__F>( \ - ::std::forward(*this)), __VA_ARGS__); \ __MACRO(const, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ __MACRO(const noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ __MACRO(const&, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ __MACRO(const& noexcept, ::pro::access_proxy<__F>(*this), __VA_ARGS__); \ - __MACRO(const&&, ::pro::access_proxy<__F>( \ - ::std::forward(*this)), __VA_ARGS__); \ - __MACRO(const&& noexcept, ::pro::access_proxy<__F>( \ - ::std::forward(*this)), __VA_ARGS__); + __MACRO(const&&, ::pro::access_proxy<__F>(::std::move(*this)), \ + __VA_ARGS__); \ + __MACRO(const&& noexcept, ::pro::access_proxy<__F>(::std::move(*this)), \ + __VA_ARGS__); #define ___PRO_ADL_ARG ::pro::details::adl_accessor_arg_t<__F, __IsDirect> #define ___PRO_DEF_FREE_ACCESSOR_TEMPLATE(__MACRO, ...) \ @@ -1274,7 +1268,6 @@ using proxy_view = proxy>; #ifdef __cpp_rtti class bad_proxy_cast : public std::bad_cast { public: - bad_proxy_cast() noexcept = default; char const* what() const noexcept override { return "pro::bad_proxy_cast"; } }; #endif // __cpp_rtti @@ -1305,7 +1298,7 @@ struct cast_dispatch_base { struct upward_conversion_dispatch : cast_dispatch_base { template - T&& operator()(T&& self) noexcept { return std::forward(self); } + T&& operator()(T&& self) const noexcept { return std::forward(self); } }; template @@ -1315,7 +1308,7 @@ struct explicit_conversion_adapter { explicit_conversion_adapter(const explicit_conversion_adapter&) = delete; template - operator U() noexcept(std::is_nothrow_constructible_v) + operator U() const noexcept(std::is_nothrow_constructible_v) requires(std::is_constructible_v) { return U{std::forward(value_)}; } @@ -1478,7 +1471,7 @@ using merge_facade_conv_t = typename add_upward_conversion_conv< struct proxy_view_dispatch : cast_dispatch_base { template - auto* operator()(T&& value) + auto* operator()(T&& value) const ___PRO_DIRECT_FUNC_IMPL(std::addressof(*std::forward(value))) }; @@ -1606,7 +1599,7 @@ struct format_dispatch { // [format.formatter.spec]. template OutIt operator()(const T& self, std::basic_string_view spec, - std::basic_format_context& fc) + std::basic_format_context& fc) const requires( #if defined(__cpp_lib_format_ranges) && __cpp_lib_format_ranges >= 202207L std::formattable @@ -1679,7 +1672,7 @@ struct proxy_cast_accessor_impl { void(proxy_cast_context) Q> {} struct proxy_cast_dispatch { template - void operator()(T&& self, proxy_cast_context ctx) { + void operator()(T&& self, proxy_cast_context ctx) const { if (typeid(T) == *ctx.type_ptr) { if (ctx.is_ref) { if constexpr (std::is_lvalue_reference_v) { @@ -1732,7 +1725,7 @@ struct wildcard { wildcard() = delete; template - [[noreturn]] operator T() { + [[noreturn]] operator T() const { #ifdef __cpp_lib_unreachable std::unreachable(); #else @@ -1860,18 +1853,18 @@ struct operator_dispatch; #define ___PRO_DEF_LHS_ALL_OP_ACCESSOR ___PRO_DEF_LHS_ANY_OP_ACCESSOR #define ___PRO_LHS_LEFT_OP_DISPATCH_BODY_IMPL(...) \ template \ - decltype(auto) operator()(T&& self) \ + decltype(auto) operator()(T&& self) const \ ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ std::forward(self)) #define ___PRO_LHS_UNARY_OP_DISPATCH_BODY_IMPL(...) \ template \ - decltype(auto) operator()(T&& self) \ + decltype(auto) operator()(T&& self) const \ ___PRO_DIRECT_FUNC_IMPL(__VA_ARGS__ std::forward(self)) \ template \ - decltype(auto) operator()(T&& self, int) \ + decltype(auto) operator()(T&& self, int) const \ ___PRO_DIRECT_FUNC_IMPL(std::forward(self) __VA_ARGS__) #define ___PRO_LHS_BINARY_OP_DISPATCH_BODY_IMPL(...) \ template \ - decltype(auto) operator()(T&& self, Arg&& arg) \ + decltype(auto) operator()(T&& self, Arg&& arg) const \ ___PRO_DIRECT_FUNC_IMPL( \ std::forward(self) __VA_ARGS__ std::forward(arg)) #define ___PRO_LHS_ALL_OP_DISPATCH_BODY_IMPL(...) \ @@ -1906,7 +1899,7 @@ ___PRO_DEBUG( \ template <> \ struct operator_dispatch<#__VA_ARGS__, true> { \ template \ - decltype(auto) operator()(T&& self, Arg&& arg) \ + decltype(auto) operator()(T&& self, Arg&& arg) const \ ___PRO_DIRECT_FUNC_IMPL( \ std::forward(arg) __VA_ARGS__ std::forward(self)) \ ___PRO_DEF_FREE_ACCESSOR_TEMPLATE( \ @@ -1953,7 +1946,7 @@ ___PRO_DEBUG( \ template <> \ struct operator_dispatch<#__VA_ARGS__, false> { \ template \ - decltype(auto) operator()(T&& self, Arg&& arg) \ + decltype(auto) operator()(T&& self, Arg&& arg) const \ ___PRO_DIRECT_FUNC_IMPL(std::forward(self) __VA_ARGS__ \ std::forward(arg)) \ ___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_LHS_ASSIGNMENT_OP_ACCESSOR, \ @@ -1962,7 +1955,7 @@ ___PRO_DEBUG( \ template <> \ struct operator_dispatch<#__VA_ARGS__, true> { \ template \ - decltype(auto) operator()(T&& self, Arg&& arg) \ + decltype(auto) operator()(T&& self, Arg&& arg) const \ ___PRO_DIRECT_FUNC_IMPL( \ std::forward(arg) __VA_ARGS__ std::forward(self)) \ ___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_RHS_ASSIGNMENT_OP_ACCESSOR, \ @@ -2007,7 +2000,7 @@ ___PRO_BINARY_OP_DISPATCH_IMPL(->*) template <> struct operator_dispatch<"()", false> { template - decltype(auto) operator()(T&& self, Args&&... args) + decltype(auto) operator()(T&& self, Args&&... args) const ___PRO_DIRECT_FUNC_IMPL( std::forward(self)(std::forward(args)...)) ___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_LHS_ANY_OP_ACCESSOR, operator()) @@ -2016,12 +2009,12 @@ template <> struct operator_dispatch<"[]", false> { #if defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202110L template - decltype(auto) operator()(T&& self, Args&&... args) + decltype(auto) operator()(T&& self, Args&&... args) const ___PRO_DIRECT_FUNC_IMPL( std::forward(self)[std::forward(args)...]) #else template - decltype(auto) operator()(T&& self, Arg&& arg) + decltype(auto) operator()(T&& self, Arg&& arg) const ___PRO_DIRECT_FUNC_IMPL(std::forward(self)[std::forward(arg)]) #endif // defined(__cpp_multidimensional_subscript) && __cpp_multidimensional_subscript >= 202110L ___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_LHS_ANY_OP_ACCESSOR, operator[]) @@ -2048,18 +2041,17 @@ struct operator_dispatch<"[]", false> { struct implicit_conversion_dispatch : details::cast_dispatch_base { template - T&& operator()(T&& self) noexcept { return std::forward(self); } + T&& operator()(T&& self) const noexcept { return std::forward(self); } }; struct explicit_conversion_dispatch : details::cast_dispatch_base { template - auto operator()(T&& self) noexcept + auto operator()(T&& self) const noexcept { return details::explicit_conversion_adapter{std::forward(self)}; } }; using conversion_dispatch = explicit_conversion_dispatch; class not_implemented : public std::exception { public: - not_implemented() noexcept = default; char const* what() const noexcept override { return "pro::not_implemented"; } }; @@ -2067,7 +2059,7 @@ template struct weak_dispatch : D { using D::operator(); template - [[noreturn]] details::wildcard operator()(std::nullptr_t, Args&&...) + [[noreturn]] details::wildcard operator()(std::nullptr_t, Args&&...) const { ___PRO_THROW(not_implemented{}); } }; @@ -2092,7 +2084,7 @@ struct weak_dispatch : D { #define ___PRO_DEF_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FNAME) \ struct __NAME { \ template \ - decltype(auto) operator()(__T&& __self, __Args&&... __args) \ + decltype(auto) operator()(__T&& __self, __Args&&... __args) const \ ___PRO_DIRECT_FUNC_IMPL(::std::forward<__T>(__self) \ .__FUNC(::std::forward<__Args>(__args)...)) \ ___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_MEM_ACCESSOR, __FNAME) \ @@ -2125,7 +2117,7 @@ ___PRO_DEBUG( \ #define ___PRO_DEF_FREE_DISPATCH_IMPL(__NAME, __FUNC, __FNAME) \ struct __NAME { \ template \ - decltype(auto) operator()(__T&& __self, __Args&&... __args) \ + decltype(auto) operator()(__T&& __self, __Args&&... __args) const \ ___PRO_DIRECT_FUNC_IMPL(__FUNC(::std::forward<__T>(__self), \ ::std::forward<__Args>(__args)...)) \ ___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_FREE_ACCESSOR, __FNAME) \ @@ -2140,7 +2132,7 @@ ___PRO_DEBUG( \ #define ___PRO_DEF_FREE_AS_MEM_DISPATCH_IMPL(__NAME, __FUNC, __FNAME) \ struct __NAME { \ template \ - decltype(auto) operator()(__T&& __self, __Args&&... __args) \ + decltype(auto) operator()(__T&& __self, __Args&&... __args) const \ ___PRO_DIRECT_FUNC_IMPL(__FUNC(::std::forward<__T>(__self), \ ::std::forward<__Args>(__args)...)) \ ___PRO_DEF_MEM_ACCESSOR_TEMPLATE(___PRO_DEF_MEM_ACCESSOR, __FNAME) \ @@ -2157,7 +2149,7 @@ ___PRO_DEBUG( \ "Use pro::weak_dispatch<" #__D "> instead.")]] __NAME : __D { \ using __D::operator(); \ template \ - decltype(auto) operator()(::std::nullptr_t, __Args&&... __args) \ + decltype(auto) operator()(::std::nullptr_t, __Args&&... __args) const \ ___PRO_DIRECT_FUNC_IMPL(__FUNC(::std::forward<__Args>(__args)...)) \ } diff --git a/samples/basic_facade_builder/support_format.cpp b/samples/basic_facade_builder/support_format.cpp new file mode 100644 index 0000000..692bca3 --- /dev/null +++ b/samples/basic_facade_builder/support_format.cpp @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from support_format.md. + +#include +#include + +#include "proxy.h" + +struct Formattable : pro::facade_builder + ::support_format + ::build {}; + +int main() { + pro::proxy p = pro::make_proxy(123); + std::cout << std::format("{}", *p) << "\n"; // Prints: "123" + std::cout << std::format("{:*<6}", *p) << "\n"; // Prints: "123***" +} diff --git a/samples/basic_facade_builder/support_rtti.cpp b/samples/basic_facade_builder/support_rtti.cpp new file mode 100644 index 0000000..ab57a8b --- /dev/null +++ b/samples/basic_facade_builder/support_rtti.cpp @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from support_rtti.md. + +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::support_direct_rtti + ::build {}; + +int main() { + int v = 123; + pro::proxy p = &v; + std::cout << proxy_typeid(p).name() << "\n"; // Prints: "Pi" (assuming GCC) + std::cout << proxy_cast(p) << "\n"; // Prints the address of v + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "i" (assuming GCC) + std::cout << proxy_cast(*p) << "\n"; // Prints: "123" +} diff --git a/samples/basic_facade_builder/support_rtti/proxy_cast.cpp b/samples/basic_facade_builder/support_rtti/proxy_cast.cpp new file mode 100644 index 0000000..978ff79 --- /dev/null +++ b/samples/basic_facade_builder/support_rtti/proxy_cast.cpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from proxy_cast.md. + +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::build {}; + +int main() { + int v = 123; + pro::proxy p; + try { + proxy_cast(*p); // Throws + } catch (const pro::bad_proxy_cast& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } + p = &v; + std::cout << proxy_cast(*p) << "\n"; // Prints: "123" + proxy_cast(*p) = 456; + std::cout << v << "\n"; // Prints: "456" + try { + proxy_cast(*p); // Throws + } catch (const pro::bad_proxy_cast& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } + int* ptr1 = proxy_cast(&*p); + std::cout << std::boolalpha << ptr1 << "\n"; // Prints an address + std::cout << std::boolalpha << &v << "\n"; // Prints the same address as above + double* ptr2 = proxy_cast(&*p); + std::cout << ptr2 << "\n"; // Prints "0" +} diff --git a/samples/basic_facade_builder/support_rtti/proxy_typeid.cpp b/samples/basic_facade_builder/support_rtti/proxy_typeid.cpp new file mode 100644 index 0000000..915bdc3 --- /dev/null +++ b/samples/basic_facade_builder/support_rtti/proxy_typeid.cpp @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from proxy_typeid.md. + +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::build {}; + +int main() { + pro::proxy p; + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "v" (assuming GCC) + p = pro::make_proxy(123); + std::cout << proxy_typeid(*p).name() << "\n"; // Prints: "i" (assuming GCC) +} diff --git a/samples/basic_facade_builder/support_view.cpp b/samples/basic_facade_builder/support_view.cpp new file mode 100644 index 0000000..a61573c --- /dev/null +++ b/samples/basic_facade_builder/support_view.cpp @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from support_view.md. + +#include +#include +#include + +#include "proxy.h" + +struct RttiAware : pro::facade_builder + ::support_rtti + ::add_view + ::add_view + ::build {}; + +int main() { + pro::proxy p = pro::make_proxy(123); + pro::proxy_view pv = p; + pro::proxy_view pcv = p; + proxy_cast(*pv) = 456; // Modifies the contained object of p + std::cout << proxy_cast(*pcv) << "\n"; // Prints: "456" +} diff --git a/samples/observer_facade.cpp b/samples/observer_facade.cpp new file mode 100644 index 0000000..a1c520f --- /dev/null +++ b/samples/observer_facade.cpp @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from observer_facade.md. + +#include + +#include "proxy.h" + +PRO_DEF_MEM_DISPATCH(MemAt, at); + +template +struct FMap : pro::facade_builder + ::add_convention + ::build {}; + +int main() { + std::map v{{1, 2}}; + + pro::proxy_view> p1 = &v; + static_assert(std::is_same_vat(1)), int&>); + p1->at(1) = 3; + printf("%d\n", v.at(1)); // Prints "3" + + pro::proxy_view> p2 = &std::as_const(v); + static_assert(std::is_same_vat(1)), const int&>); + // p2->at(1) = 4; won't compile + printf("%d\n", p2->at(1)); // Prints "3" +} diff --git a/samples/weak_dispatch.cpp b/samples/weak_dispatch.cpp new file mode 100644 index 0000000..f9d9e80 --- /dev/null +++ b/samples/weak_dispatch.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// This file contains example code from weak_dispatch.md. + +#include +#include +#include + +#include "proxy.h" + +PRO_DEF_MEM_DISPATCH(MemAt, at); + +struct WeakDictionary : pro::facade_builder + ::add_convention, std::string(int index) const> + ::build {}; + +int main() { + std::vector v{"hello", "world"}; + pro::proxy p1 = &v; + std::cout << p1->at(1) << "\n"; // Prints: "world" + pro::proxy p2 = pro::make_proxy(123); + try { + p2->at(1); + } catch (const pro::not_implemented& e) { + std::cout << e.what() << "\n"; // Prints an explanatory string + } +}