Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)

project(msft_proxy VERSION 3.2.1 LANGUAGES CXX)
project(msft_proxy VERSION 3.3.0 LANGUAGES CXX)
add_library(msft_proxy INTERFACE)
target_compile_features(msft_proxy INTERFACE cxx_std_20)
target_include_directories(msft_proxy INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.githu

### Hello World

Let's get started with the following "Hello World" example:
Let's get started with the following "Hello World" example ([run](https://godbolt.org/z/jr81Poz83)):

```cpp
#include <format>
Expand Down Expand Up @@ -80,7 +80,7 @@ Here is a step-by-step explanation:

### 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:
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 ([run](https://godbolt.org/z/q1b14WGff)):

```cpp
#include <iomanip>
Expand Down Expand Up @@ -136,7 +136,7 @@ In addition to the operator expressions demonstrated in the previous examples, t
- [class template `pro::operator_dispatch`](https://microsoft.github.io/proxy/docs/operator_dispatch.html): Dispatch type for operator 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:
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 ([run](https://godbolt.org/z/xcEeYrjnY)):

```cpp
#include <iostream>
Expand Down Expand Up @@ -205,11 +205,13 @@ 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.
- **Concepts**: To facilitate template programming with "Proxy", 3 concepts are exported from a facade type. Namely, [`proxiable`](https://microsoft.github.io/proxy/docs/proxiable.html), [`proxiable_target`](https://microsoft.github.io/proxy/docs/proxiable_target.html) and [`inplace_proxiable_target`](https://microsoft.github.io/proxy/docs/inplace_proxiable_target.html).
- **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 [alias template `proxy_view`, class template `observer_facade`](https://microsoft.github.io/proxy/docs/proxy_view.html) and [`facade_builder::support_view`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_view.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.0. Please refer to [function template `make_proxy_view`](https://microsoft.github.io/proxy/docs/make_proxy_view.html), [alias template `proxy_view`, class template `observer_facade`](https://microsoft.github.io/proxy/docs/proxy_view.html) and [`facade_builder::support_view`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_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.
- **Shared and weak ownership**: Although `proxy` can be created from a [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr), extensions are available to create `proxy` objects with shared and weak ownership in a more efficient way since 3.3.0. Please refer to [function template `make_proxy_shared`](https://microsoft.github.io/proxy/docs/make_proxy_shared.html), [`allocate_proxy_shared`](https://microsoft.github.io/proxy/docs/allocate_proxy_shared.html), [alias template `weak_proxy`, class template `weak_facade`](https://microsoft.github.io/proxy/docs/weak_proxy.html) and [`facade_builder::support_weak`](https://microsoft.github.io/proxy/docs/basic_facade_builder/support_weak.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.

## <a name="compiler-req">Minimum Requirements for Compilers</a>
Expand Down
16 changes: 8 additions & 8 deletions docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ struct dispatch_name {
return func_name(std::forward<T>(self), std::forward<Args>(args)...);
}

template <class F, class C, class... Os>
template <class F, bool IsDirect, class D, class... Os>
struct accessor {
accessor() = delete;
};
template <class F, class C, class... Os>
requires(sizeof...(Os) > 1u && (std::is_constructible_v<accessor<F, C, Os>> && ...))
struct accessor<F, C, Os...> : accessor<F, C, Os>... {
using accessor<F, C, Os>::accessibility_func_name ...;
template <class F, bool IsDirect, class D, class... Os>
requires(sizeof...(Os) > 1u && (std::is_constructible_v<accessor<F, IsDirect, D, Os>> && ...))
struct accessor<F, IsDirect, D, Os...> : accessor<F, IsDirect, D, Os>... {
using accessor<F, IsDirect, D, Os>::accessibility_func_name ...;
};
template <class F, class C, class R, class... Args>
struct accessor<F, C, R(Args...) cv ref noex> {
template <class F, bool IsDirect, class D, class R, class... Args>
struct accessor<F, IsDirect, D, R(Args...) cv ref noex> {
R accessibility_func_name(Args... args) cv ref noex {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
return pro::proxy_invoke<IsDirect, D, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion docs/ProReflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ A type `R` meets the *ProReflection* requirements of a type `P` if `R` meets the

## See Also

[*ProFacade* requirements](ProFacade.md)
- [*ProFacade* requirements](ProFacade.md)
10 changes: 5 additions & 5 deletions docs/access_proxy.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Function template `access_proxy`

```cpp
template <class F, class A>
template <facade F, class A>
proxy<F>& access_proxy(A& a) noexcept;

template <class F, class A>
template <facade F, class A>
const proxy<F>& access_proxy(const A& a) noexcept;

template <class F, class A>
template <facade F, class A>
proxy<F>&& access_proxy(A&& a) noexcept;

template <class F, class A>
template <facade F, class A>
const proxy<F>&& access_proxy(const A&& a) noexcept;
```

Accesses a `proxy` object from an [accessor](ProAccessible.md) instantiated from the `proxy`. `F` shall model concept [`facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing distinct types `Cs`. There shall be a type `C` in `Cs` where `A` is the same type as `typename C::template accessor<F>`. The behavior is undefined if `a` is not instantiated from a `proxy`.
Accesses a `proxy` object from an [accessor](ProAccessible.md) instantiated from the `proxy`. As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing distinct types `Cs`. There shall be a type `C` in `Cs` where `A` is the same type as `typename C::template accessor<F>`. The behavior is undefined if `a` is not instantiated from a `proxy`.

## Return Value

Expand Down
2 changes: 2 additions & 0 deletions docs/allocate_proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ proxy<F> allocate_proxy(const Alloc& alloc, T&& value); // freestanding-deleted

`(3)` Creates a `proxy<F>` object containing a value `p` of type `allocated-ptr<std::decay_t<T>, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.

*Since 3.3.0*: For `(1-3)`, if [`proxiable_target<std::decay_t<T>, F>`](proxiable_target.md) is `false`, the program is ill-formed and diagnostic messages are generated.

## Return Value

The constructed `proxy` object.
Expand Down
65 changes: 65 additions & 0 deletions docs/allocate_proxy_shared.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Function template `allocate_proxy_shared`

The definition of `allocate_proxy_shared` makes use of exposition-only class templates *strong-compact-ptr* and *weak-compact-ptr*. Their semantics are similar to [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) and [`std::weak_ptr`](https://en.cppreference.com/w/cpp/memory/weak_ptr), but do not provide a polymorphic deleter. Their size and alignment are guaranteed not to be greater than those of a raw pointer type. `strong-compact-ptr<T, Alloc>` is conditionally convertible to `weak-compact-ptr<T, Alloc>` only if necessary. Similar to [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional), `strong-compact-ptr<T, Alloc>` provides `operator*` for accessing the managed object of type `T` with the same qualifiers.

```cpp
// (1)
template <facade F, class T, class Alloc, class... Args>
proxy<F> allocate_proxy_shared(const Alloc& alloc, Args&&... args); // freestanding-deleted

// (2)
template <facade F, class T, class Alloc, class U, class... Args>
proxy<F> allocate_proxy_shared(const Alloc& alloc, std::initializer_list<U> il, Args&&... args); // freestanding-deleted

// (3)
template <facade F, class Alloc, class T>
proxy<F> allocate_proxy_shared(const Alloc& alloc, T&& value); // freestanding-deleted
```

`(1)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<Args>(args)...`.

`(2)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<T, Alloc>`, where `*p` is direct-non-list-initialized with `il, std::forward<Args>(args)...`.

`(3)` Creates a `proxy<F>` object containing a value `p` of type `strong-compact-ptr<std::decay_t<T>, Alloc>`, where `*p` is direct-non-list-initialized with `std::forward<T>(value)`.

For `(1-3)`, if [`proxiable_target<std::decay_t<T>, F>`](proxiable_target.md) is `false`, the program is ill-formed and diagnostic messages are generated.

## Return Value

The constructed `proxy` object.

## Exceptions

Throws any exception thrown by allocation or the constructor of `T`.

## Notes

The implementation of `strong-compact-ptr` may vary depending on the definition of `F`. Specifically, when `F` does not support weak ownership via [`basic_facade_builder::support_weak`](basic_facade_builder/support_weak.md), `strong-compact-ptr<T, Alloc>` is not convertible to `weak-compact-ptr<T, Alloc>`, which leaves more room for optimization.

## Example

```cpp
#include <iostream>
#include <memory_resource>

#include "proxy.h"

struct RttiAware : pro::facade_builder
::support_copy<pro::constraint_level::nothrow>
::support_rtti
::build {};

int main() {
std::pmr::unsynchronized_pool_resource pool;
std::pmr::polymorphic_allocator<> alloc{&pool};
pro::proxy<RttiAware> p1 = pro::allocate_proxy_shared<RttiAware>(alloc, 1);
pro::proxy<RttiAware> p2 = p1;
proxy_cast<int&>(*p1) += 2;
std::cout << proxy_cast<int>(*p2) << "\n"; // Prints "3"
}
```

## See Also

- [function template `allocate_proxy`](allocate_proxy.md)
- [named requirements *Allocator*](https://en.cppreference.com/w/cpp/named_req/Allocator)
1 change: 1 addition & 0 deletions docs/basic_facade_builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using facade_builder = basic_facade_builder<std::tuple<>, std::tuple<>,
| [`support_format`<br />`support_wformat`](basic_facade_builder/support_format.md)<br />*(since 3.2.0)* | Specifies the capability of formatting (via [formatting functions](https://en.cppreference.com/w/cpp/utility/format)) to the template parameters |
| [`support_rtti`<br />`support_indirect_rtti`<br />`support_direct_rtti`](basic_facade_builder/support_rtti.md)<br />*(since 3.2.0)* | Specifies the capability of RTTI (via `proxy_cast` and `proxy_typeid`) to the template parameters |
| [`support_view` ](basic_facade_builder/support_view.md)<br />*(since 3.2.1)* | Specifies the capability of implicit conversion to `proxy_view` to the template parameters |
| [`support_weak`](basic_facade_builder/support_weak.md)<br />*(since 3.3.0)* | Specifies the capability of implicit conversion to `weak_proxy` to the template parameters |

## Member Alias Templates

Expand Down
43 changes: 43 additions & 0 deletions docs/basic_facade_builder/support_weak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# `basic_facade_builder::support_weak`

```cpp
using support_weak = basic_facade_builder</* see below */>;
```

The member type `support_weak` of `basic_facade_builder<Cs, Rs, C>` adds necessary convention types to allow implicit conversion from [`proxy`](../proxy.md)`<F>` to [`weak_proxy`](../weak_proxy.md)`<F>` where `F` is a [facade](../facade.md) type built from `basic_facade_builder`.

Let `p` be a value of type `proxy<F>`, `ptr` of type `P` be the contained value of `p` (if any), the conversion from type `const proxy<F>&` to type `weak_proxy<F>` is equivalent to `return typename P::weak_type{ptr}` if `p` contains a value, or otherwise equivalent to `return nullptr`.

## Notes

`support_weak` is compatible with [`std::weak_ptr`](https://en.cppreference.com/w/cpp/memory/weak_ptr), and may generate more efficient code when working with [`make_proxy_shared`](../make_proxy_shared.md) or [`allocate_proxy_shared`](../allocate_proxy_shared.md). It is also compatible with any custom shared/weak ownership implementations if `typename P::weak_type{ptr}` is well-formed.

## Example

```cpp
#include <iostream>

#include "proxy.h"

struct Formattable : pro::facade_builder
::support_format
::support_weak
::build {};

int main() {
pro::proxy<Formattable> p1 = pro::make_proxy_shared<Formattable>(123);
pro::weak_proxy<Formattable> wp = p1;
pro::proxy<Formattable> p2 = wp.lock();
std::cout << std::boolalpha << p2.has_value() << "\n"; // Prints "true"
std::cout << std::format("{}\n", *p2); // Prints "123"

p1.reset();
p2.reset();
p2 = wp.lock();
std::cout << p2.has_value() << "\n"; // Prints "false"
}
```

## See Also

- [`add_convention`](add_convention.md)
2 changes: 1 addition & 1 deletion docs/facade_aware_overload_t.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Class template `facade_aware_overload_t<O>` specifies a facade-aware overload te
template <class F>
using BinaryOverload = pro::proxy<F>(const pro::proxy_indirect_accessor<F>& rhs) const;

template <class T, class F>
template <class T, pro::facade F>
pro::proxy<F> operator+(const T& value, const pro::proxy_indirect_accessor<F>& rhs)
requires(!std::is_same_v<T, pro::proxy_indirect_accessor<F>>)
{ return pro::make_proxy<F, T>(value + proxy_cast<const T&>(rhs)); }
Expand Down
2 changes: 1 addition & 1 deletion docs/formatter_proxy_indirect_accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

```cpp
namespace std {
template <class F, class CharT> requires(/* see below */)
template <facade F, class CharT> requires(/* see below */)
struct formatter<pro::proxy_indirect_accessor<F>, ChatT>; // since 3.2.0
}
```
Expand Down
1 change: 0 additions & 1 deletion docs/implicit_conversion_dispatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ Class `implicit_conversion_dispatch` models a [dispatch](ProDispatch.md) type fo
## Example

```cpp
#include <iomanip>
#include <iostream>

#include "proxy.h"
Expand Down
6 changes: 3 additions & 3 deletions docs/inplace_proxiable_target.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

```cpp
template <class T, class F>
concept inplace_proxiable_target = proxiable</* inplace-ptr<T> */, F>;
concept inplace_proxiable_target = proxiable<inplace-ptr<T>, F>;
```

The concept `inplace_proxiable_target<T, F>` specifies that a value type `T`, when wrapped by an implementation-defined non-allocating pointer type, models a contained value type of [`proxy<F>`](proxy.md). The size and alignment of this implementation-defined pointer type are guaranteed to be equal to those of type `T`.
See [`make_proxy_inplace`](make_proxy_inplace.md) for the definition of the exposition-only class template *inplace-ptr*.

## Example

Expand All @@ -31,4 +31,4 @@ int main() {
## See Also

- [concept `proxiable`](proxiable.md)
- [function template `make_proxy_inplacce`](make_proxy_inplace.md)
- [concept `proxiable_target`](proxiable_target.md)
Loading