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.28)

project(msft_proxy4 VERSION 4.0.0 LANGUAGES CXX)
project(msft_proxy4 VERSION 4.0.1 LANGUAGES CXX)
add_library(msft_proxy4 INTERFACE)
set_target_properties(msft_proxy4 PROPERTIES EXPORT_NAME proxy)
add_library(msft_proxy4::proxy ALIAS msft_proxy4)
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,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 ([run](https://godbolt.org/z/3G363xz71)):
Let's get started with the following "Hello World" example ([run](https://godbolt.org/z/f7W36f3se)):

```cpp
#include <format>
Expand All @@ -61,7 +61,7 @@ struct Formattable : pro::facade_builder
::build {};

int main() {
std::string str = "Hello World";
static std::string str = "Hello World";
pro::proxy<Formattable> p1 = &str;
std::cout << std::format("*p1 = {}\n", *p1); // Prints "*p1 = Hello World"

Expand Down Expand Up @@ -98,7 +98,7 @@ Note: If you prefer the library to be consumed as a (C++20) module, refer to [C+

### Hello World (Stream Version)

In the previous "Hello World" 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/xcsM3v3cY)):
In the previous "Hello World" 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/447aMbrbj)):

```cpp
#include <iomanip>
Expand All @@ -112,7 +112,7 @@ struct Streamable : pro::facade_builder
::build {};

int main() {
std::string str = "Hello World";
static std::string str = "Hello World";
pro::proxy<Streamable> p1 = &str;
std::cout << "*p1 = " << *p1 << "\n"; // Prints "p1 = Hello World"

Expand Down Expand Up @@ -254,6 +254,7 @@ ctest --test-dir build -j

## Related Resources

- August, 2025: [Announcing Proxy 4: The Next Leap in C++ Polymorphism](https://devblogs.microsoft.com/cppblog/announcing-proxy-4-the-next-leap-in-c-polymorphism/)
- May, 2025: [Published ISO C++ proposal P3086R34 Proxy: A Pointer-Semantics-Based Polymorphism Library](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3086r4.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/)
Expand Down
1 change: 1 addition & 0 deletions docs/spec/.pages
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nav:
- proxy_indirect_accessor: proxy_indirect_accessor.md
- proxy_view<br />observer_facade: proxy_view.md
- proxy: proxy
- substitution_dispatch: substitution_dispatch
- weak_dispatch: weak_dispatch
- weak_proxy<br />weak_facade: weak_proxy.md
- Alias Templates:
Expand Down
1 change: 1 addition & 0 deletions docs/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This document provides the API specifications for the C++ library Proxy (version
| [`proxy_indirect_accessor`](proxy_indirect_accessor.md) | Provides indirection accessibility for `proxy` |
| [`proxy_view`<br />`observer_facade`](proxy_view.md) | Non-owning `proxy` optimized for raw pointer types |
| [`proxy`](proxy/README.md) | Wraps a pointer object matching specified facade |
| [`substitution_dispatch`](substitution_dispatch/README.md) | Dispatch type for `proxy` substitution with accessibility |
| [`weak_dispatch`](weak_dispatch/README.md) | Weak dispatch type with a default implementation that throws `not_implemented` |
| [`weak_proxy`<br />`weak_facade`](weak_proxy.md) | `proxy` with weak ownership |

Expand Down
8 changes: 4 additions & 4 deletions docs/spec/basic_facade_builder/add_facade.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# `basic_facade_builder::add_facade`

```cpp
template <facade F, bool WithUpwardConversion = false>
template <facade F, bool WithSubstitution = false>
using add_facade = basic_facade_builder</* see below */>;
```

Expand All @@ -14,11 +14,11 @@ The alias template `add_facade` of `basic_facade_builder<Cs, Rs, MaxSize, MaxAli
- sets `Copyability` to `std::max(Copyability, F::copyability)`, and
- sets `Relocatability` to `std::max(Relocatability, F::relocatability)`, and
- sets `Destructibility` to `std::max(Destructibility, F::destructibility)`, and
- optionally, adds a convention for implicit upward conversion into `Cs` when `WithUpwardConversion` is `true`.
- optionally, merges a direct convention of [`substitution_dispatch`](../substitution_dispatch/README.md) into `Cs` when `WithSubstitution` is `true`.

## Notes

Adding a facade type that contains duplicated convention or reflection types already defined in `Cs` or `Rs` is well-defined and does not have side effects on [`build`](build.md) at either compile-time or runtime. By default, `WithUpwardConversion` is `false`, which guarantees minimal binary size in code generation. However, upward conversion is helpful when an API requires backward compatibility. Users can opt-in to this feature by specifying `true` as the second parameter of `add_facade`, at the cost of potentially a slightly larger binary size.
Adding a facade type that contains duplicated convention or reflection types already defined in `Cs` or `Rs` is well-defined and does not have side effects on [`build`](build.md) at either compile-time or runtime. By default, `WithSubstitution` is `false`, which guarantees minimal binary size in code generation. However, substitution is helpful when an API requires backward compatibility. Users can opt-in to this feature by specifying `true` as the second parameter of `add_facade`, at the cost of potentially a slightly larger binary size.

## Example

Expand Down Expand Up @@ -68,7 +68,7 @@ int main() {
auto p2 = p1; // Performs a deep copy
p2->emplace(456, "trivial");

// Performs an upward conversion from an rvalue reference
// Performs a substitution from an rvalue reference
pro::proxy<StringDictionary> p3 = std::move(p2);
std::cout << p1->size() << "\n"; // Prints "1"
std::cout << p1->at(123) << "\n"; // Prints "lalala"
Expand Down
2 changes: 1 addition & 1 deletion docs/spec/explicit_conversion_dispatch/accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct accessor<P, D, Os...> : accessor<P, D, Os>... {
};

// (3)
template <class P, class D>
template <class P, class D, class T>
struct accessor<P, D, T() cv ref noex> {
explicit operator T() cv ref noex;
};
Expand Down
2 changes: 1 addition & 1 deletion docs/spec/implicit_conversion_dispatch/accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct accessor<P, D, Os...> : accessor<P, D, Os>... {
};

// (3)
template <class P, class D>
template <class P, class D, class T>
struct accessor<P, D, T() cv ref noex> {
operator T() cv ref noex;
};
Expand Down
1 change: 1 addition & 0 deletions docs/spec/msft_lib_proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Starting with 3.0.0, Proxy ships a feature-test macro that encodes the library v

| Version | Value of `__msft_lib_proxy` |
| ------- | --------------------------- |
| 4.0.1 | `202510L` |
| 4.0.0 | `202508L` |
| 3.4.0 | `202505L` |
| 3.3.0 | `202503L` |
Expand Down
8 changes: 5 additions & 3 deletions docs/spec/proxy_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ template <facade F>
using proxy_view = proxy<observer_facade<F>>;
```

Class template `observer_facade` is a [facade](facade.md) type for observer pointers (e.g., raw pointers) potentially dereferenced from a `proxy` object.
`proxy_view<F>` is a non-owning, trivially copyable, trivially relocatable view of an object that models [`proxiable_target<T, F>`](proxiable_target.md). It behaves like a `proxy<F>` except that it never owns the lifetime of the underlying object.

`observer_facade<F>` adapts an existing [facade](facade.md) `F` for this non-owning use. The adaptation preserves only those parts of `F` that remain semantically valid when the storage is reduced to a single pointer and modifies substitution conversions so that view-ness is preserved (substitution that would have produced an owning `proxy<G>` instead produces a `proxy_view<G>`).

## 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, for each convention type `C` defined in `typename F::convention_types`, `C` is reserved when `C::is_direct` is `false` or when `C` is an upward conversion convention added via [`basic_facade_builder::add_facade<?, true>`](basic_facade_builder/add_facade.md), 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. |
| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::convention_types`. Specifically, for each convention `C` in `typename F::convention_types`:<br/> - If `C::is_direct` is `false`, include `C` unchanged.<br/> - Otherwise, if `typename C::dispatch_type` is [`substitution_dispatch`](./substitution_dispatch/README.md), include a transformed convention `C'` whose<br/> * `is_direct` is `true` and `dispatch_type` is still `substitution_dispatch`.<br/> * For every overload `O` in `typename C::overload_types` with signature (after cv/ref/noexcept qualifiers) returning a `proxy<G>`, replace its return type with `proxy_view<G>` while preserving qualifiers and `noexcept`.<br/> - Otherwise `C` is discarded. |
| `reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type transformed from `typename F::reflection_types`. Specifically, for each reflection type `R` in `typename F::reflection_types`, `R` is included when `R::is_direct` is `false`, or otherwise discarded. |

## Member Constants of `observer_facade`

Expand Down
4 changes: 4 additions & 0 deletions docs/spec/substitution_dispatch/.pages
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
nav:
- substitution_dispatch: README.md
- accessor: accessor.md
- operator(): operator_call.md
71 changes: 71 additions & 0 deletions docs/spec/substitution_dispatch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Class `substitution_dispatch`

> Header: `proxy.h`
> Module: `proxy`
> Namespace: `pro::inline v4`
> Since: 4.0.1

```cpp
class substitution_dispatch;
```

Class `substitution_dispatch` models a [dispatch](../ProDispatch.md) type for `proxy` substitution. It meets the [*ProAccessible* requirements](../ProAccessible.md) of applicable types.

## Member Functions

| Name | Description |
| -------------------------------- | -------------------------------------------- |
| (constructor) [nothrow] | constructs an `substitution_dispatch` object |
| [`operator()`](operator_call.md) | invokes the dispatch |

## Member Types

| Name | Description |
| ------------------------- | --------------------------------- |
| [`accessor`](accessor.md) | provides accessibility to `proxy` |

## Example

```cpp
#include <iostream>

#include <proxy/proxy.h>

struct Runnable : pro::facade_builder //
::add_convention<pro::operator_dispatch<"()">, void()> //
::build {};

struct CopyableRunnable : pro::facade_builder //
::support_copy<pro::constraint_level::nontrivial> //
::add_facade<Runnable> //
::add_direct_convention<pro::substitution_dispatch,
pro::proxy<Runnable>() const&,
pro::proxy<Runnable>() &&> //
::build {};

int main() {
pro::proxy<CopyableRunnable> p1 = pro::make_proxy<CopyableRunnable>(
[] { std::cout << "Lambda expression invoked\n"; });

// Implicit conversion via const reference of pro::proxy<CopyableRunnable>
pro::proxy<Runnable> p2 = p1;
std::cout << std::boolalpha << p2.has_value() << "\n"; // Prints "true"

// Implicit conversion via rvalue reference of pro::proxy<CopyableRunnable>
pro::proxy<Runnable> p3 = std::move(p1);
std::cout << p1.has_value() << "\n"; // Prints "false"
(*p3)(); // Prints "Lambda expression invoked"

// Different from implicit_conversion_dispatch, substitution from a null proxy
// is well-formed
pro::proxy<Runnable> p4 = p1;
std::cout << p4.has_value() << "\n"; // Prints "false"
}
```

## See Also

- [`basic_facade_builder::add_facade`](../basic_facade_builder/add_facade.md)
- [alias template `skills::as_view`](../skills_as_view.md)
- [alias template `skills::as_weak`](../skills_as_weak.md)
- [class `implicit_conversion_dispatch`](../implicit_conversion_dispatch/README.md)
28 changes: 28 additions & 0 deletions docs/spec/substitution_dispatch/accessor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Class template `substitution_dispatch::accessor`

```cpp
// (1)
template <class P, class D, class... Os>
struct accessor {
accessor() = delete;
};

// (2)
template <class P, class D, class... Os>
requires(sizeof...(Os) > 1u && (std::is_constructible_v<accessor<P, D, Os>> && ...))
struct accessor<P, D, Os...> : accessor<P, D, Os>... {
using accessor<P, D, Os>::operator return-type-of<Os>...;
};

// (3)
template <class P, class D, facade F>
struct accessor<P, D, proxy<F>() cv ref noex> {
operator proxy<F>() cv ref noex;
};
```

`(1)` The default implementation of `accessor` is not constructible.

`(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<P, D, Os>...` are default-constructible, inherits all `accessor<P, D, Os>...` types and `using` their `operator return-type-of<Os>`. `return-type-of<O>` denotes the *return type* of the overload type `O`.

`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `proxy<F>() cv ref noex`, provides an implicit `operator proxy<F>()` with the same *cv ref noex* specifiers. `accessor::operator proxy<F>()` is equivalent to `return proxy_invoke<proxy<F>() cv ref noex>(static_cast<P cv <ref ? ref : &>>(*this))` when `static_cast<const P&>(*this).has_value()` is `true`, or `return nullptr` otherwise.
8 changes: 8 additions & 0 deletions docs/spec/substitution_dispatch/operator_call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `substitution_dispatch::operator()`

```cpp
template <class T>
T&& operator()(T&& value) const noexcept; // exposition-only
```

Returns `std::forward<T>(value)`. When `T` is not cv- or ref-qualified and [`is_bitwise_trivially_relocatable_v<T>`](../is_bitwise_trivially_relocatable.md) is `true`, conversion from the return value to any `proxy` type shall perform bitwise trivial relocation and does not require that `T` is move-constructible.
14 changes: 10 additions & 4 deletions docs/spec/weak_proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ template <facade F>
using weak_proxy = proxy<weak_facade<F>>;
```

Class template `weak_facade` is a [facade](facade.md) type for weak pointers (e.g., [`std::weak_ptr`](https://en.cppreference.com/w/cpp/memory/weak_ptr)) potentially converted from a `proxy` object.
`weak_proxy<F>` is a non-owning handle that participates in the weak ownership model of an object that (when alive) models [`proxiable_target<T, F>`](proxiable_target.md). It is analogous to `std::weak_ptr` relative to `std::shared_ptr`: it never extends the lifetime of the underlying object, but it can attempt to produce a (strong) `proxy<F>` via `lock()`.

`weak_facade<F>` adapts the original [facade](facade.md) `F` so that:

* A `lock()` member (direct convention) is provided, returning a `proxy<F>` that contains a value if and only if the referenced object is still alive at the time of the call.
* All direct substitution conversions that would have produced a `proxy<G>` become conversions that produce a `weak_proxy<G>` instead (so that "weak-ness" is preserved across facade-substitution).
* No reflections from `F` are preserved.

## Member Types of `weak_facade`

| Name | Description |
| ------------------ | ------------------------------------------------------------ |
| `convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains one convention type `C`, where`C::is_direct` is `false`, `C::dispatch_type` is of member function `lock` with accessibility, and `C::overload_types` is a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains type `proxy<F>() const noexcept`. |
| 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: <br/>- It always prepends a direct convention whose dispatch type denotes the member function `lock` and whose single overload has signature `proxy<F>() const noexcept`. Calling this overload attempts to obtain a strong `proxy<F>`; it returns an empty `proxy<F>` if the object has expired.<br/>- For any direct convention `C` in `F` whose `dispatch_type` is `substitution_dispatch`, a transformed convention `C'` is included, whose<br/> * `is_direct` is `true` and `dispatch_type` is still `substitution_dispatch`.<br/> * For every overload `O` in `typename C::overload_types` with signature (after cv/ref/noexcept qualifiers) returning a `proxy<G>`, replace its return type with `weak_proxy<G>` while preserving qualifiers and `noexcept`.<br/>- All other conventions from `F` are discarded. |
| `reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains no types. |

## Member Constants of `weak_facade`
Expand Down
18 changes: 10 additions & 8 deletions include/proxy/v4/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,20 +405,20 @@ consteval bool diagnose_proxiable_required_convention_not_implemented() {

template <class F, bool IsDirect, class D, class O>
struct invocation_meta {
constexpr invocation_meta() = default;
invocation_meta() = default;
template <class P>
constexpr explicit invocation_meta(std::in_place_type_t<P>) noexcept
constexpr explicit invocation_meta(std::in_place_type_t<P>)
: dispatcher(overload_traits<O>::template dispatcher<P, F, IsDirect, D>) {
}

typename overload_traits<O>::template dispatcher_type<F> dispatcher;
};

template <class... Ms>
struct composite_meta : Ms... {
constexpr composite_meta() noexcept = default;
struct PRO4D_ENFORCE_EBO composite_meta : Ms... {
composite_meta() = default;
template <class P>
constexpr explicit composite_meta(std::in_place_type_t<P>) noexcept
constexpr explicit composite_meta(std::in_place_type_t<P>)
: Ms(std::in_place_type<P>)... {}
};

Expand Down Expand Up @@ -500,6 +500,7 @@ struct conv_traits

template <bool IsDirect, class R>
struct refl_meta {
refl_meta() = default;
template <class P>
requires(IsDirect)
constexpr explicit refl_meta(std::in_place_type_t<P>)
Expand All @@ -511,6 +512,7 @@ struct refl_meta {
std::in_place_type<typename std::pointer_traits<P>::element_type>) {
}

[[PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE]]
R reflector;
};

Expand Down Expand Up @@ -829,9 +831,9 @@ using ptr_prototype = void* [2];

template <class M>
struct meta_ptr_indirect_impl {
constexpr meta_ptr_indirect_impl() = default;
meta_ptr_indirect_impl() = default;
template <class P>
constexpr explicit meta_ptr_indirect_impl(std::in_place_type_t<P>) noexcept
explicit meta_ptr_indirect_impl(std::in_place_type_t<P>)
: ptr_(&storage<P>) {}
bool has_value() const noexcept { return ptr_ != nullptr; }
void reset() noexcept { ptr_ = nullptr; }
Expand Down Expand Up @@ -2250,10 +2252,10 @@ struct proxy_cast_dispatch {
#undef PROD_DEF_PROXY_CAST_ACCESSOR

struct proxy_typeid_reflector {
proxy_typeid_reflector() = default;
template <class T>
constexpr explicit proxy_typeid_reflector(std::in_place_type_t<T>)
: info(&typeid(T)) {}
constexpr proxy_typeid_reflector(const proxy_typeid_reflector&) = default;

template <class Self, class R>
struct accessor {
Expand Down
Loading