From eea7350dd072322a96674f580142c9a71038fa32 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Wed, 22 Oct 2025 21:18:06 +0800 Subject: [PATCH 01/14] Improve constraints for facade (#359) --- docs/spec/ProBasicConvention.md | 2 +- docs/spec/ProBasicFacade.md | 6 +- docs/spec/ProBasicReflection.md | 2 +- docs/spec/basic_facade_builder/README.md | 4 +- .../basic_facade_builder/restrict_layout.md | 4 +- .../spec/basic_facade_builder/support_copy.md | 3 +- .../support_destruction.md | 3 +- .../support_relocation.md | 3 +- include/proxy/v4/proxy.h | 17 ++- tests/proxy_traits_tests.cpp | 112 ++++++++++++++---- 10 files changed, 121 insertions(+), 35 deletions(-) diff --git a/docs/spec/ProBasicConvention.md b/docs/spec/ProBasicConvention.md index 06a749f..1b4dc24 100644 --- a/docs/spec/ProBasicConvention.md +++ b/docs/spec/ProBasicConvention.md @@ -5,7 +5,7 @@ A type `C` meets the *ProBasicConvention* requirements if the following expressi | Expressions | Semantics | | ---------------------------- | ------------------------------------------------------------ | | `C::is_direct` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `bool`, specifying whether the convention applies to a pointer type itself (`true`), or the element type of a pointer type (`false`). | -| `typename C::dispatch_type` | A [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType) that defines how the calls are forwarded to the concrete types. | +| `typename C::dispatch_type` | A type that defines how the calls are forwarded to the concrete types. Shall be *nothrow-default-constructible* and *nothrow-destructible*. | | `typename C::overload_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of one or more distinct types `Os`. Each type `O` in `Os` shall meet the [*ProOverload* requirements](ProOverload.md). | ## See Also diff --git a/docs/spec/ProBasicFacade.md b/docs/spec/ProBasicFacade.md index 85fd877..f454c9c 100644 --- a/docs/spec/ProBasicFacade.md +++ b/docs/spec/ProBasicFacade.md @@ -6,12 +6,14 @@ A type `F` meets the *ProBasicFacade* requirements if the following expressions | ------------------------------ | ------------------------------------------------------------ | | `typename F::convention_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Cs`. Each type `C` in `Cs` shall meet the [*ProBasicConvention* requirements](ProBasicConvention.md). | | `typename F::reflection_types` | A [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type that contains any number of distinct types `Rs`. Each type `R` in `Rs` shall define reflection data structure. | -| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum size of a pointer type. | -| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum alignment of a pointer type. | +| `F::max_size` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum size of a pointer type. Shall be greater than `0` and a multiple of `F::max_align`. | +| `F::max_align` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `std::size_t` that defines the maximum alignment of a pointer type. Shall be a power of `2`. | | `F::copyability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required copyability of a pointer type. | | `F::relocatability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of a pointer type. | | `F::destructibility` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of a pointer type. | +Each of `F::copyability`, `F::relocatability`, and `F::destructibility` shall be exactly one of the four enumerators of `constraint_level` (`none`, `nontrivial`, `nothrow`, `trivial`). + ## See Also - [concept `facade`](facade.md) diff --git a/docs/spec/ProBasicReflection.md b/docs/spec/ProBasicReflection.md index 771885b..32296b5 100644 --- a/docs/spec/ProBasicReflection.md +++ b/docs/spec/ProBasicReflection.md @@ -7,7 +7,7 @@ A type `R` meets the *ProBasicReflection* requirements if the following expressi | Expressions | Semantics | | ---------------------------- | ------------------------------------------------------------ | | `R::is_direct` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `bool`, specifying whether the reflection applies to a pointer type itself (`true`), or the element type of a pointer type (`false`). | -| `typename R::reflector_type` | A [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType) that defines the data structure reflected from the type. | +| `typename R::reflector_type` | A type that defines the data structure reflected from the type. Shall be *nothrow-default-constructible* and *nothrow-destructible*. | ## See Also diff --git a/docs/spec/basic_facade_builder/README.md b/docs/spec/basic_facade_builder/README.md index 0e1defc..2ee5ea2 100644 --- a/docs/spec/basic_facade_builder/README.md +++ b/docs/spec/basic_facade_builder/README.md @@ -12,7 +12,7 @@ constexpr constraint_level default-cl = static_cast( std::numeric_limits>::min()); // exposition only ``` -Given a [facade](../facade.md ) type `F`, any meaningful value of `F::max_size` and `F::max_align` is less than *default-size*; any meaningful value of `F::copyability`, `F::relocatability`, and `F::destructibility` is greater than *default-cl*. +Given a [facade](../facade.md) type `F`, any meaningful value of `F::max_size` and `F::max_align` is less than *default-size*; any meaningful value of `F::copyability`, `F::relocatability`, and `F::destructibility` is greater than *default-cl*. ```cpp template - requires(std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) + requires(PtrSize > 0u && std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) using restrict_layout = basic_facade_builder; ``` -The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t`). After applying the restriction, `MaxSize` becomes `std::min(MaxSize, PtrSize)`, and `MaxAlign` becomes `std::min(C::max_align, MaxAlign)`. +The alias template `restrict_layout` of `basic_facade_builder` adds layout restrictions to the template parameters. The default value of `PtrAlign` is the maximum possible alignment of an object of size `PtrSize`, not greater than `alignof(std::max_align_t)`. After applying the restriction, `MaxSize` becomes `std::min(MaxSize, PtrSize)`, and `MaxAlign` becomes `std::min(C::max_align, MaxAlign)`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_copy.md b/docs/spec/basic_facade_builder/support_copy.md index 0223306..6fac5f0 100644 --- a/docs/spec/basic_facade_builder/support_copy.md +++ b/docs/spec/basic_facade_builder/support_copy.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_copy = basic_facade_builder; ``` -The alias template `support_copy` of `basic_facade_builder` adds copyability support to the template parameters. After the operation, `Copyability` becomes `std::max(Copyability, CL)`. +The alias template `support_copy` of `basic_facade_builder` adds copyability support to the template parameters. After the operation, `Copyability` becomes `std::max(Copyability, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_destruction.md b/docs/spec/basic_facade_builder/support_destruction.md index cda2e5a..8dd134c 100644 --- a/docs/spec/basic_facade_builder/support_destruction.md +++ b/docs/spec/basic_facade_builder/support_destruction.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_destruction = basic_facade_builder; ``` -The alias template `support_destruction` of `basic_facade_builder` adds destruction support to the template parameters. After the operation, `Destructibility` becomes `std::max(Destructibility, CL)`. +The alias template `support_destruction` of `basic_facade_builder` adds destruction support to the template parameters. After the operation, `Destructibility` becomes `std::max(Destructibility, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/docs/spec/basic_facade_builder/support_relocation.md b/docs/spec/basic_facade_builder/support_relocation.md index 78e9b15..d3b0e60 100644 --- a/docs/spec/basic_facade_builder/support_relocation.md +++ b/docs/spec/basic_facade_builder/support_relocation.md @@ -2,10 +2,11 @@ ```cpp template + requires(/* see below */) using support_relocation = basic_facade_builder; ``` -The alias template `support_relocation` of `basic_facade_builder` adds relocatability support to the template parameters. After the operation, `Relocatability` becomes `std::max(Relocatability, CL)`. +The alias template `support_relocation` of `basic_facade_builder` adds relocatability support to the template parameters. After the operation, `Relocatability` becomes `std::max(Relocatability, CL)`. The expression inside `requires` is equivalent to `CL` is a defined enumerator of `constraint_level`. ## Notes diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index fec34fd..8f3113f 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -680,6 +680,12 @@ consteval bool diagnose_proxiable_insufficient_destructibility() { return verdict; } +consteval bool is_layout_well_formed(std::size_t size, std::size_t align) { + return size > 0u && std::has_single_bit(align) && size % align == 0u; +} +consteval bool is_cl_well_formed(constraint_level cl) { + return cl >= constraint_level::none && cl <= constraint_level::trivial; +} template consteval bool is_facade_constraints_well_formed() { if constexpr (requires { @@ -693,8 +699,10 @@ consteval bool is_facade_constraints_well_formed() { return std::tuple{F::max_size, F::max_align, F::copyability, F::relocatability, F::destructibility}; })) { - return std::has_single_bit(F::max_align) && - F::max_size % F::max_align == 0u; + return is_layout_well_formed(F::max_size, F::max_align) && + is_cl_well_formed(F::copyability) && + is_cl_well_formed(F::relocatability) && + is_cl_well_formed(F::destructibility); } } return false; @@ -2044,22 +2052,25 @@ struct basic_facade_builder { details::merge_constraint(Destructibility, F::destructibility)>; template - requires(std::has_single_bit(PtrAlign) && PtrSize % PtrAlign == 0u) + requires(details::is_layout_well_formed(PtrSize, PtrAlign)) using restrict_layout = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_copy = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_relocation = basic_facade_builder; template + requires(details::is_cl_well_formed(CL)) using support_destruction = basic_facade_builder); -struct BadFacade_BadConstraints_BadAlignment { - using convention_types = std::tuple<>; - using reflection_types = std::tuple<>; - static constexpr std::size_t max_size = 6u; - static constexpr std::size_t max_align = 6u; // Should be a power of 2 - static constexpr auto copyability = pro::constraint_level::none; - static constexpr auto relocatability = pro::constraint_level::nothrow; - static constexpr auto destructibility = pro::constraint_level::nothrow; -}; -static_assert(!pro::facade); +// Well-formed facade_impl specialization +static_assert( + pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); -struct BadFacade_BadConstraints_BadSize { - using convention_types = std::tuple<>; - using reflection_types = std::tuple<>; - static constexpr std::size_t max_size = - 6u; // Should be a multiple of max_alignment - static constexpr std::size_t max_align = 4u; - static constexpr auto copyability = pro::constraint_level::none; - static constexpr auto relocatability = pro::constraint_level::nothrow; - static constexpr auto destructibility = pro::constraint_level::nothrow; -}; -static_assert(!pro::facade); +// Bad size (max_size should be positive) +static_assert( + !pro::facade, std::tuple<>, 0, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad size (max_size should be a multiple of max_align) +static_assert( + !pro::facade, std::tuple<>, 10, 4, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad alignment (max_align should be a power of 2) +static_assert( + !pro::facade, std::tuple<>, 6, 6, pro::constraint_level::none, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad copyability (less than constraint_level::none) +static_assert( + !pro::facade, std::tuple<>, 8, 4, (pro::constraint_level)-1, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad copyability (greater than constraint_level::trivial) +static_assert( + !pro::facade, std::tuple<>, 8, 4, (pro::constraint_level)100, + pro::constraint_level::trivial, pro::constraint_level::nothrow>>); + +// Bad relocatability (less than constraint_level::none) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + (pro::constraint_level)-1, pro::constraint_level::nothrow>>); + +// Bad relocatability (greater than constraint_level::trivial) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + (pro::constraint_level)100, pro::constraint_level::nothrow>>); + +// Bad destructibility (less than constraint_level::none) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, (pro::constraint_level)-1>>); + +// Bad destructibility (greater than constraint_level::trivial) +static_assert(!pro::facade, std::tuple<>, 8, 4, pro::constraint_level::none, + pro::constraint_level::trivial, (pro::constraint_level)100>>); struct BadFacade_BadConstraints_NotConstant { using convention_types = std::tuple<>; @@ -402,6 +435,43 @@ static_assert(pro::facade); static_assert(FacadeWithSizeOfNonPowerOfTwo::max_size == 6u); static_assert(FacadeWithSizeOfNonPowerOfTwo::max_align == 2u); +template +concept IsFacadeBuilderWellFormedWithGivenLayout = + requires { typename pro::facade_builder::restrict_layout; }; +static_assert(IsFacadeBuilderWellFormedWithGivenLayout<6u, 1u>); +static_assert(!IsFacadeBuilderWellFormedWithGivenLayout<6u, 3u>); +static_assert(!IsFacadeBuilderWellFormedWithGivenLayout<1u, 2u>); + +template +concept IsFacadeBuilderWellFormedWithGivenCopyability = + requires { typename pro::facade_builder::support_copy; }; +static_assert( + IsFacadeBuilderWellFormedWithGivenCopyability); +static_assert( + !IsFacadeBuilderWellFormedWithGivenCopyability<(pro::constraint_level)-1>); +static_assert( + !IsFacadeBuilderWellFormedWithGivenCopyability<(pro::constraint_level)100>); + +template +concept IsFacadeBuilderWellFormedWithGivenRelocatability = + requires { typename pro::facade_builder::support_relocation; }; +static_assert(IsFacadeBuilderWellFormedWithGivenRelocatability< + pro::constraint_level::none>); +static_assert(!IsFacadeBuilderWellFormedWithGivenRelocatability< + (pro::constraint_level)-1>); +static_assert(!IsFacadeBuilderWellFormedWithGivenRelocatability< + (pro::constraint_level)100>); + +template +concept IsFacadeBuilderWellFormedWithGivenDestructibility = + requires { typename pro::facade_builder::support_destruction; }; +static_assert(IsFacadeBuilderWellFormedWithGivenDestructibility< + pro::constraint_level::none>); +static_assert(!IsFacadeBuilderWellFormedWithGivenDestructibility< + (pro::constraint_level)-1>); +static_assert(!IsFacadeBuilderWellFormedWithGivenDestructibility< + (pro::constraint_level)100>); + static_assert(!std::is_default_constructible_v< pro::proxy_indirect_accessor>); static_assert( From 9bc70d29a59af33cc3abc18d30b226e3758c565b Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 10 Nov 2025 19:36:19 -1000 Subject: [PATCH 02/14] Remove support for standard trivial relocation (#365) --- docs/spec/is_bitwise_trivially_relocatable.md | 8 +--- include/proxy/v4/proxy.h | 40 +++++-------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/docs/spec/is_bitwise_trivially_relocatable.md b/docs/spec/is_bitwise_trivially_relocatable.md index 0fbcd51..a344ea6 100644 --- a/docs/spec/is_bitwise_trivially_relocatable.md +++ b/docs/spec/is_bitwise_trivially_relocatable.md @@ -16,7 +16,7 @@ constexpr bool is_bitwise_trivially_relocatable_v = The class template `is_bitwise_trivially_relocatable` is a type trait whose `value` is `true` when objects of (complete) type `T` can be *bitwise trivially relocated*: a new object of type `T` can be created at an arbitrary suitably aligned storage location by performing a raw byte-wise copy (as if by `std::memcpy`) of `sizeof(T)` bytes from the original object's storage, and the original object can then be considered destroyed (its lifetime ends) without invoking its destructor. Otherwise the `value` is `false`. -Semantics follow the model described in [P3780R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3780r0.html), not the `std::trivially_relocatable` facility from [P2786R13](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2786r13.html) (C++26). The library keeps this separate trait for portability and because its internal optimizations rely on the "memcpy relocation" guarantee. +Semantics follow the model described in [P3780R0](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3780r0.html). The library keeps this separate trait for portability and because its internal optimizations rely on the "memcpy relocation" guarantee. ## Definition @@ -45,12 +45,6 @@ These specializations reflect empirical knowledge of the representations of comm ## Notes -### Relationship to `std::trivially_relocatable` - -C++26 `std::trivially_relocatable` may encompass additional nuances (it can be satisfied by types whose relocation uses compiler transformations beyond an as-if byte copy). A type for which this trait is `true` is always a good candidate to be `std::trivially_relocatable`, but the converse is not required by this library. - -### Customization Guidelines - You may provide additional specializations of `is_bitwise_trivially_relocatable` (in namespace `pro`) to opt in types you own. A correct specialization must ensure the type does not depend on its *address* remaining stable (self-pointers, intrusive container hooks, pointer provenance, etc.). A positive specialization is a promise you must uphold. Violating the contract results in undefined behavior in any operation that uses the fast relocation path (e.g., certain `proxy` conversions or assignments). diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index 8f3113f..117b64b 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -46,12 +46,6 @@ #define PROD_UNREACHABLE() std::abort() #endif // __cpp_lib_unreachable >= 202202L -#if __cpp_trivial_relocatability >= 202502L -#define PROD_TR_IF_ELIGIBLE trivially_relocatable_if_eligible -#else -#define PROD_TR_IF_ELIGIBLE -#endif // __cpp_trivial_relocatability >= 202502L - namespace pro::inline v4 { // ============================================================================= @@ -630,12 +624,6 @@ struct refl_accessor_traits template using refl_accessor_t = typename refl_accessor_traits::type; -struct tr_blocker { - tr_blocker() = default; - tr_blocker(const tr_blocker&) noexcept {} - tr_blocker& operator=(const tr_blocker&) noexcept { return *this; } -}; - template struct ptr_traits : inapplicable_traits {}; template @@ -797,11 +785,9 @@ struct facade_traits using indirect_accessor = composite_t; - using direct_accessor = composite_t< - typename facade_traits::conv_direct_accessor, - typename facade_traits::refl_direct_accessor, - std::conditional_t>; + using direct_accessor = + composite_t; template static consteval void diagnose_proxiable() { @@ -934,9 +920,8 @@ class proxy_indirect_accessor }; template -class proxy PROD_TR_IF_ELIGIBLE - : public details::facade_traits::direct_accessor, - public details::inplace_ptr> { +class proxy : public details::facade_traits::direct_accessor, + public details::inplace_ptr> { friend struct details::proxy_helper; static_assert(details::facade_traits::applicable); @@ -1559,9 +1544,8 @@ class indirect_ptr { }; template -class PRO4D_ENFORCE_EBO allocated_ptr PROD_TR_IF_ELIGIBLE - : private alloc_aware, - public indirect_ptr> { +class PRO4D_ENFORCE_EBO allocated_ptr : private alloc_aware, + public indirect_ptr> { public: template allocated_ptr(const Alloc& alloc, Args&&... args) @@ -1588,8 +1572,7 @@ struct PRO4D_ENFORCE_EBO compact_ptr_storage : alloc_aware, inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class compact_ptr PROD_TR_IF_ELIGIBLE - : public indirect_ptr> { +class compact_ptr : public indirect_ptr> { using Storage = compact_ptr_storage; public: @@ -1621,7 +1604,7 @@ struct PRO4D_ENFORCE_EBO shared_compact_ptr_storage inplace_ptr(std::in_place, std::forward(args)...) {} }; template -class shared_compact_ptr PROD_TR_IF_ELIGIBLE +class shared_compact_ptr : public indirect_ptr> { using Storage = shared_compact_ptr_storage; @@ -1660,7 +1643,7 @@ struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base, template class weak_compact_ptr; template -class strong_compact_ptr PROD_TR_IF_ELIGIBLE +class strong_compact_ptr : public indirect_ptr> { using Storage = strong_weak_compact_ptr_storage; friend class weak_compact_ptr; @@ -1701,7 +1684,7 @@ class strong_compact_ptr PROD_TR_IF_ELIGIBLE const T&& operator*() const&& noexcept { return std::move(*operator->()); } }; template -class weak_compact_ptr PROD_TR_IF_ELIGIBLE { +class weak_compact_ptr { public: weak_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { @@ -2653,7 +2636,6 @@ struct formatter, CharT> { } // namespace std #endif // __STDC_HOSTED__ && __has_include() -#undef PROD_TR_IF_ELIGIBLE #undef PROD_UNREACHABLE #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE From cf7576b86892311977743b6292f98fc4ebcc084c Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 17 Nov 2025 12:37:23 +0800 Subject: [PATCH 03/14] Avoid false positive for ptr_traits (#367) --- docs/spec/ProFacade.md | 9 +++++++++ docs/spec/proxy/assignment.md | 2 +- docs/spec/proxy/constructor.md | 6 +++--- docs/spec/proxy/emplace.md | 4 ++-- include/proxy/v4/proxy.h | 10 +++++++--- tests/proxy_traits_tests.cpp | 14 ++++++++++++++ 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/docs/spec/ProFacade.md b/docs/spec/ProFacade.md index 34582fa..1ca2515 100644 --- a/docs/spec/ProFacade.md +++ b/docs/spec/ProFacade.md @@ -12,6 +12,15 @@ A type `F` meets the *ProFacade* requirements of a type `P` if `F` meets the [*P | `F::relocatability` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required relocatability of `P`. | | `F::destructibility` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type [`constraint_level`](constraint_level.md) that defines the required destructibility of `P`. | +*Since 4.0.2*: `P` shall be a pointer-like type eligible for `proxy`. A type `P` is eligible if `P` is not a specialization of `proxy` and the following condition is satisfied: + +```cpp +(requires { *std::declval(); } || requires { typename P::element_type; }) && +requires { typename std::pointer_traits

::element_type; } +``` + +In other words, `P` either supports dereferencing or provides an `element_type`, and `std::pointer_traits

` yields a valid `element_type`. + ## Notes Relocatability is defined as *move-construct an object and then destroy the original instance*. Specifically, the value of `F::relocatability` maps to the following requirements on `P`: diff --git a/docs/spec/proxy/assignment.md b/docs/spec/proxy/assignment.md index a96426f..d3558bc 100644 --- a/docs/spec/proxy/assignment.md +++ b/docs/spec/proxy/assignment.md @@ -38,7 +38,7 @@ Assigns a new value to `proxy` or destroys the contained value. - `(1)` Destroys the current contained value if it exists. After the call, `*this` does not contain a value. - `(2)` Copy assignment operator copies the contained value of `rhs` to `*this`. If `rhs` does not contain a value, it destroys the contained value of `*this` (if any) as if by `auto(rhs).swap(*this)`. The copy assignment is trivial when `F::copyability == constraint_level::trivial` is `true`. - `(3)` Move assignment operator moves the contained value of `rhs` to `*this`. If `rhs` does not contain a value, it destroys the contained value of `*this` (if any). If the move construction throws when `F::relocatability == constraint_level::nontrivial`, `*this` does not contain a value. After move assignment, `rhs` is in a valid state with an unspecified value. The move assignment operator does not participate in overload resolution when `F::copyability == constraint_level::trivial`, falling back to the trivial copy assignment operator. -- `(4)` Let `VP` be `std::decay_t

`. Sets the contained value to an object of type `VP`, direct-non-list-initialized with `std::forward

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

` is not a specialization of `proxy`. *Since 3.3.0*: If [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. +- `(4)` Let `VP` be `std::decay_t

`. Sets the contained value to an object of type `VP`, direct-non-list-initialized with `std::forward

(ptr)`. Participates in overload resolution only if `VP` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). *Since 3.3.0*: If [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and a diagnostic is generated. ## Return Value diff --git a/docs/spec/proxy/constructor.md b/docs/spec/proxy/constructor.md index 076e517..61f9182 100644 --- a/docs/spec/proxy/constructor.md +++ b/docs/spec/proxy/constructor.md @@ -44,9 +44,9 @@ 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::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::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)`. This overload participates in overload resolution only if `std::decay_t

` is not a specialization of `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)...`. +- `(4)` Let `VP` be `std::decay_t

`. Constructs a `proxy` whose contained value is of type `VP`, direct-non-list-initialized with `std::forward

(ptr)`. Participates in overload resolution only if `VP` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). +- `(5)` Constructs a `proxy` whose contained value is of type `P`, direct-non-list-initialized with `std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. +- `(6)` Constructs a `proxy` whose contained value is of type `P`, direct-non-list-initialized with `il, std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. *Since 3.3.0*: For `(4-6)`, if [`proxiable, F>`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. diff --git a/docs/spec/proxy/emplace.md b/docs/spec/proxy/emplace.md index c5570b3..587c00c 100644 --- a/docs/spec/proxy/emplace.md +++ b/docs/spec/proxy/emplace.md @@ -23,8 +23,8 @@ The `emplace` function templates change the contained value to an object of type First, the current contained value (if any) is destroyed as if by calling [reset()](reset.md). Then: -- `(1)` Sets the contained value to an object of type `P` and direct-non-list-initialized with `std::forward(args)...`. -- `(2)` Sets the contained value to an object of type `P` and direct-non-list-initialized with `il, std::forward(args)...`. +- `(1)` Sets the contained value to an object of type `P`, direct-non-list-initialized with `std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy` (see [*ProFacade* requirements](../ProFacade.md)). +- `(2)` Sets the contained value to an object of type `P`, direct-non-list-initialized with `il, std::forward(args)...`. Participates in overload resolution only if `P` is a pointer-like type eligible for `proxy`. *Since 3.3.0*: For `(1-2)`, if [`proxiable`](../proxiable.md) is `false`, the program is ill-formed and diagnostic messages are generated. diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index 117b64b..f1881d7 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -627,12 +627,13 @@ using refl_accessor_t = typename refl_accessor_traits::type; template struct ptr_traits : inapplicable_traits {}; template - requires(requires { typename std::pointer_traits

::element_type; }) + requires(( + requires { *std::declval(); } || + requires { typename P::element_type; }) && + requires { typename std::pointer_traits

::element_type; }) struct ptr_traits

: applicable_traits {}; template struct ptr_traits> : inapplicable_traits {}; -template <> -struct ptr_traits : inapplicable_traits {}; template consteval bool diagnose_proxiable_size_too_large() { @@ -908,6 +909,7 @@ add_qualifier_t, Q> template concept proxiable = facade && details::facade_traits::applicable && + details::ptr_traits

::applicable && details::facade_traits::template applicable_ptr

; template @@ -1686,6 +1688,8 @@ class strong_compact_ptr template class weak_compact_ptr { public: + using element_type = T; + weak_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { ptr_->weak_count.fetch_add(1, std::memory_order::relaxed); diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index 6f1ff43..8e81a4e 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -483,4 +483,18 @@ static_assert( static_assert( !std::is_move_assignable_v>); +// proxy shall not be constructible from an arbitrary class template +// instantiation. See https://github.com/microsoft/proxy/issues/366 +template +struct ProxyWrapperTemplate { + explicit ProxyWrapperTemplate(pro::proxy p) + : p_(std::move(p)) {} + + pro::proxy p_; +}; +static_assert(!pro::proxiable); +static_assert(!std::is_constructible_v, + ProxyWrapperTemplate>); +static_assert(std::is_move_constructible_v>); + } // namespace proxy_traits_tests_details From bcd341d3988d3521ef06a1a46aa90f44a951478b Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 17 Nov 2025 12:37:44 +0800 Subject: [PATCH 04/14] Fix overload resolution in `PRO_DEF_FREE_AS_MEM_DISPATCH` (#372) --- include/proxy/v4/proxy_macros.h | 2 +- tests/proxy_invocation_tests.cpp | 53 +++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/include/proxy/v4/proxy_macros.h b/include/proxy/v4/proxy_macros.h index c21c80a..147474a 100644 --- a/include/proxy/v4/proxy_macros.h +++ b/include/proxy/v4/proxy_macros.h @@ -141,7 +141,7 @@ PRO4D_STATIC_CALL(decltype(auto), ProT&& pro_self, ProArgs&&... pro_args) \ PRO4D_DIRECT_FUNC_IMPL(impl(::std::forward(pro_self), \ ::std::forward(pro_args)...)) \ - PRO4D_DEF_ACCESSOR_TEMPLATE(FREE, PRO4D_DEF_MEM_ACCESSOR, func) \ + PRO4D_DEF_ACCESSOR_TEMPLATE(MEM, PRO4D_DEF_MEM_ACCESSOR, func) \ } #define PRO4D_DEF_FREE_AS_MEM_DISPATCH_2(name, impl) \ PRO4D_DEF_FREE_AS_MEM_DISPATCH_IMPL(name, impl, impl) diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 6664720..83350e9 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -161,6 +161,9 @@ std::string Dump(T&& value) noexcept { PRO_DEF_FREE_DISPATCH(FreeDump, Dump); +PRO_DEF_FREE_DISPATCH(FreeInvoke, std::invoke, Invoke); +PRO_DEF_FREE_AS_MEM_DISPATCH(MemInvoke, std::invoke, Invoke); + } // namespace proxy_invocation_tests_details namespace details = proxy_invocation_tests_details; @@ -247,7 +250,7 @@ TEST(ProxyInvocationTests, TestRecursiveDefinition) { ASSERT_EQ(sum, 21); } -TEST(ProxyInvocationTests, TestOverloadResolution) { +TEST(ProxyInvocationTests, TestOverloadResolution_Member) { struct OverloadedCallable : pro::facade_builder // ::add_convention, void(int), void(double), @@ -272,6 +275,54 @@ TEST(ProxyInvocationTests, TestOverloadResolution) { ASSERT_FALSE((std::is_invocable_v>)); } +TEST(ProxyInvocationTests, TestOverloadResolution_Free) { + struct OverloadedCallable + : pro::facade_builder // + ::add_convention // + ::build {}; + std::vector side_effect; + auto p = pro::make_proxy([&](auto&&... args) { + side_effect = details::GetTypeIndices...>(); + }); + Invoke(*p, 123); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, 1.23); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + char foo[2]; + Invoke(*p, foo); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, "lalala"); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + Invoke(*p, "lalala", 0); + ASSERT_EQ(side_effect, (details::GetTypeIndices())); +} + +TEST(ProxyInvocationTests, TestOverloadResolution_FreeAsMem) { + struct OverloadedInvocable + : pro::facade_builder // + ::add_convention // + ::build {}; + std::vector side_effect; + auto p = pro::make_proxy([&](auto&&... args) { + side_effect = details::GetTypeIndices...>(); + }); + p->Invoke(123); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke(1.23); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + char foo[2]; + p->Invoke(foo); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke("lalala"); + ASSERT_EQ(side_effect, details::GetTypeIndices()); + p->Invoke("lalala", 0); + ASSERT_EQ(side_effect, (details::GetTypeIndices())); +} + TEST(ProxyInvocationTests, TestNoexcept) { std::vector side_effect; auto p = pro::make_proxy>( From 789dd193a4329dc706f7ce54bc8f6e165c78dd11 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 17 Nov 2025 12:38:07 +0800 Subject: [PATCH 05/14] Simplify the implementation of `strong_compact_ptr` (#368) --- include/proxy/v4/proxy.h | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index f1881d7..e502b17 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -1645,45 +1645,42 @@ struct strong_weak_compact_ptr_storage : strong_weak_compact_ptr_storage_base, template class weak_compact_ptr; template -class strong_compact_ptr - : public indirect_ptr> { +class strong_compact_ptr { using Storage = strong_weak_compact_ptr_storage; friend class weak_compact_ptr; public: using weak_type = weak_compact_ptr; - explicit strong_compact_ptr(Storage* ptr) noexcept - : indirect_ptr(ptr) {} + explicit strong_compact_ptr(Storage* ptr) noexcept : ptr_(ptr) {} template strong_compact_ptr(const Alloc& alloc, Args&&... args) - : indirect_ptr( - allocate(alloc, alloc, std::forward(args)...)) {} - strong_compact_ptr(const strong_compact_ptr& rhs) noexcept - : indirect_ptr(rhs.ptr_) { - this->ptr_->strong_count.fetch_add(1, std::memory_order::relaxed); + : ptr_(allocate(alloc, alloc, std::forward(args)...)) {} + strong_compact_ptr(const strong_compact_ptr& rhs) noexcept : ptr_(rhs.ptr_) { + ptr_->strong_count.fetch_add(1, std::memory_order::relaxed); } strong_compact_ptr(strong_compact_ptr&& rhs) = delete; ~strong_compact_ptr() noexcept(std::is_nothrow_destructible_v) { - if (this->ptr_->strong_count.fetch_sub(1, std::memory_order::acq_rel) == - 1) { + if (ptr_->strong_count.fetch_sub(1, std::memory_order::acq_rel) == 1) { std::destroy_at(operator->()); - if (this->ptr_->weak_count.fetch_sub(1u, std::memory_order::release) == - 1) { - deallocate(this->ptr_->alloc, this->ptr_); + if (ptr_->weak_count.fetch_sub(1u, std::memory_order::release) == 1) { + deallocate(ptr_->alloc, ptr_); } } } T* operator->() noexcept { - return std::launder(reinterpret_cast(&this->ptr_->value)); + return std::launder(reinterpret_cast(&ptr_->value)); } const T* operator->() const noexcept { - return std::launder(reinterpret_cast(&this->ptr_->value)); + return std::launder(reinterpret_cast(&ptr_->value)); } T& operator*() & noexcept { return *operator->(); } const T& operator*() const& noexcept { return *operator->(); } T&& operator*() && noexcept { return std::move(*operator->()); } const T&& operator*() const&& noexcept { return std::move(*operator->()); } + +private: + strong_weak_compact_ptr_storage* ptr_; }; template class weak_compact_ptr { From 1abcf538457319f86a99ed6ed0edd77edfa259eb Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 17 Nov 2025 12:38:22 +0800 Subject: [PATCH 06/14] Fix wording for proxy indirection (#369) --- docs/spec/proxy/indirection.md | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/docs/spec/proxy/indirection.md b/docs/spec/proxy/indirection.md index 578a7d2..4bcdf1f 100644 --- a/docs/spec/proxy/indirection.md +++ b/docs/spec/proxy/indirection.md @@ -1,23 +1,15 @@ # `proxy::operator->`
`proxy::operator*` -The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant: - -```cpp -static constexpr bool has-indirection = see below; // exposition only -``` - -As per [`facade`](../facade.md), `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`. Let `Cs2` be the types in `Cs` where each type `C` meets the [*ProAccessible* requirements](../ProAccessible.md) of `F` and `C::is_direct` is `false`. *has-indirection* is `true` if `Cs2` contains at least one type; otherwise, it is `false`. *indirect-accessor* is a non-copyable type that inherits from every type in `Cs2`. - ```cpp // (1) -proxy_indirect_accessor* operator->() noexcept requires(has-indirection); -const proxy_indirect_accessor* operator->() const noexcept requires(has-indirection); +proxy_indirect_accessor* operator->() noexcept; +const proxy_indirect_accessor* operator->() const noexcept; // (2) -proxy_indirect_accessor& operator*() & noexcept requires(has-indirection); -const proxy_indirect_accessor& operator*() const& noexcept requires(has-indirection); -proxy_indirect_accessor&& operator*() && noexcept requires(has-indirection); -const proxy_indirect_accessor&& operator*() const&& noexcept requires(has-indirection); +proxy_indirect_accessor& operator*() & noexcept; +const proxy_indirect_accessor& operator*() const& noexcept; +proxy_indirect_accessor&& operator*() && noexcept; +const proxy_indirect_accessor&& operator*() const&& noexcept; ``` These operators access the accessors of the indirect conventions, as if dereferencing the contained value. From f88a3a3bcf0f5e87b5817a3619753a982885ef82 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 20 Nov 2025 12:34:56 +0800 Subject: [PATCH 07/14] Revise benchmarks (#373) --- .github/workflows/bvt-clang.yml | 2 +- benchmarks/CMakeLists.txt | 6 +- ...hmark.cpp => proxy_creation_benchmark.cpp} | 65 ++--- benchmarks/proxy_invocation_benchmark.cpp | 129 --------- .../proxy_invocation_benchmark_context.h | 36 --- benchmarks/proxy_operation_benchmark.cpp | 267 ++++++++++++++++++ ... => proxy_operation_benchmark_context.cpp} | 47 ++- .../proxy_operation_benchmark_context.h | 49 ++++ tools/report_generator/main.cpp | 119 +++++--- tools/report_generator/report-config.json | 248 +++++++++------- 10 files changed, 614 insertions(+), 354 deletions(-) rename benchmarks/{proxy_management_benchmark.cpp => proxy_creation_benchmark.cpp} (82%) delete mode 100644 benchmarks/proxy_invocation_benchmark.cpp delete mode 100644 benchmarks/proxy_invocation_benchmark_context.h create mode 100644 benchmarks/proxy_operation_benchmark.cpp rename benchmarks/{proxy_invocation_benchmark_context.cpp => proxy_operation_benchmark_context.cpp} (72%) create mode 100644 benchmarks/proxy_operation_benchmark_context.h diff --git a/.github/workflows/bvt-clang.yml b/.github/workflows/bvt-clang.yml index 3ead78b..84c6c2c 100644 --- a/.github/workflows/bvt-clang.yml +++ b/.github/workflows/bvt-clang.yml @@ -21,7 +21,7 @@ jobs: - name: build and run test with clang 20 run: | cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) + mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) echo "Running clang-format on ${#FILES[@]} files: ${FILES[*]}" clang-format-20 --dry-run --Werror "${FILES[@]}" cmake --build build -j diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index d39821c..882f104 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -17,9 +17,9 @@ set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "Disable google benchmark unit t FetchContent_MakeAvailable(benchmark) add_executable(msft_proxy_benchmarks - proxy_invocation_benchmark_context.cpp - proxy_invocation_benchmark.cpp - proxy_management_benchmark.cpp + proxy_creation_benchmark.cpp + proxy_operation_benchmark_context.cpp + proxy_operation_benchmark.cpp ) target_include_directories(msft_proxy_benchmarks PRIVATE .) target_link_libraries(msft_proxy_benchmarks PRIVATE msft_proxy4::proxy benchmark::benchmark benchmark::benchmark_main) diff --git a/benchmarks/proxy_management_benchmark.cpp b/benchmarks/proxy_creation_benchmark.cpp similarity index 82% rename from benchmarks/proxy_management_benchmark.cpp rename to benchmarks/proxy_creation_benchmark.cpp index 25dbb44..a17a4e4 100644 --- a/benchmarks/proxy_management_benchmark.cpp +++ b/benchmarks/proxy_creation_benchmark.cpp @@ -30,7 +30,7 @@ constexpr int TestManagedObjectCount = 600000; constexpr int TypeSeriesCount = 3; using SmallObject1 = int; -using SmallObject2 = std::shared_ptr; +using SmallObject2 = double; struct SmallObject3 { SmallObject3() noexcept = default; SmallObject3(SmallObject3&&) noexcept = default; @@ -38,7 +38,7 @@ struct SmallObject3 { throw std::runtime_error{"Not implemented"}; } - std::unique_lock Field1; + std::unique_ptr Field1; }; using LargeObject1 = std::array; @@ -58,9 +58,10 @@ struct PolymorphicObject : PolymorphicObjectBase { struct DefaultFacade : pro::facade_builder // ::support_copy // + ::add_skill // ::build {}; -void BM_SmallObjectManagementWithProxy(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -73,7 +74,7 @@ void BM_SmallObjectManagementWithProxy(benchmark::State& state) { } } -void BM_SmallObjectManagementWithProxy_Shared(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy_Shared(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -86,7 +87,7 @@ void BM_SmallObjectManagementWithProxy_Shared(benchmark::State& state) { } } -void BM_SmallObjectManagementWithProxy_SharedPooled(benchmark::State& state) { +void BM_SmallObjectCreationWithProxy_SharedPooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -104,7 +105,7 @@ void BM_SmallObjectManagementWithProxy_SharedPooled(benchmark::State& state) { } } -void BM_SmallObjectManagementWithUniquePtr(benchmark::State& state) { +void BM_SmallObjectCreationWithUniquePtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -120,7 +121,7 @@ void BM_SmallObjectManagementWithUniquePtr(benchmark::State& state) { } } -void BM_SmallObjectManagementWithSharedPtr(benchmark::State& state) { +void BM_SmallObjectCreationWithSharedPtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -133,7 +134,7 @@ void BM_SmallObjectManagementWithSharedPtr(benchmark::State& state) { } } -void BM_SmallObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { +void BM_SmallObjectCreationWithSharedPtr_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -148,7 +149,7 @@ void BM_SmallObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { } } -void BM_SmallObjectManagementWithAny(benchmark::State& state) { +void BM_SmallObjectCreationWithAny(benchmark::State& state) { for (auto _ : state) { std::vector data; data.reserve(TestManagedObjectCount); @@ -161,7 +162,7 @@ void BM_SmallObjectManagementWithAny(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -174,7 +175,7 @@ void BM_LargeObjectManagementWithProxy(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_Pooled(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -189,7 +190,7 @@ void BM_LargeObjectManagementWithProxy_Pooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_Shared(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_Shared(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -202,7 +203,7 @@ void BM_LargeObjectManagementWithProxy_Shared(benchmark::State& state) { } } -void BM_LargeObjectManagementWithProxy_SharedPooled(benchmark::State& state) { +void BM_LargeObjectCreationWithProxy_SharedPooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -220,7 +221,7 @@ void BM_LargeObjectManagementWithProxy_SharedPooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithUniquePtr(benchmark::State& state) { +void BM_LargeObjectCreationWithUniquePtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -236,7 +237,7 @@ void BM_LargeObjectManagementWithUniquePtr(benchmark::State& state) { } } -void BM_LargeObjectManagementWithSharedPtr(benchmark::State& state) { +void BM_LargeObjectCreationWithSharedPtr(benchmark::State& state) { for (auto _ : state) { std::vector> data; data.reserve(TestManagedObjectCount); @@ -249,7 +250,7 @@ void BM_LargeObjectManagementWithSharedPtr(benchmark::State& state) { } } -void BM_LargeObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { +void BM_LargeObjectCreationWithSharedPtr_Pooled(benchmark::State& state) { static std::pmr::unsynchronized_pool_resource pool; std::pmr::polymorphic_allocator<> alloc{&pool}; for (auto _ : state) { @@ -264,7 +265,7 @@ void BM_LargeObjectManagementWithSharedPtr_Pooled(benchmark::State& state) { } } -void BM_LargeObjectManagementWithAny(benchmark::State& state) { +void BM_LargeObjectCreationWithAny(benchmark::State& state) { for (auto _ : state) { std::vector data; data.reserve(TestManagedObjectCount); @@ -277,20 +278,20 @@ void BM_LargeObjectManagementWithAny(benchmark::State& state) { } } -BENCHMARK(BM_SmallObjectManagementWithProxy); -BENCHMARK(BM_SmallObjectManagementWithProxy_Shared); -BENCHMARK(BM_SmallObjectManagementWithProxy_SharedPooled); -BENCHMARK(BM_SmallObjectManagementWithUniquePtr); -BENCHMARK(BM_SmallObjectManagementWithSharedPtr); -BENCHMARK(BM_SmallObjectManagementWithSharedPtr_Pooled); -BENCHMARK(BM_SmallObjectManagementWithAny); -BENCHMARK(BM_LargeObjectManagementWithProxy); -BENCHMARK(BM_LargeObjectManagementWithProxy_Pooled); -BENCHMARK(BM_LargeObjectManagementWithProxy_Shared); -BENCHMARK(BM_LargeObjectManagementWithProxy_SharedPooled); -BENCHMARK(BM_LargeObjectManagementWithUniquePtr); -BENCHMARK(BM_LargeObjectManagementWithSharedPtr); -BENCHMARK(BM_LargeObjectManagementWithSharedPtr_Pooled); -BENCHMARK(BM_LargeObjectManagementWithAny); +BENCHMARK(BM_SmallObjectCreationWithProxy); +BENCHMARK(BM_SmallObjectCreationWithProxy_Shared); +BENCHMARK(BM_SmallObjectCreationWithProxy_SharedPooled); +BENCHMARK(BM_SmallObjectCreationWithUniquePtr); +BENCHMARK(BM_SmallObjectCreationWithSharedPtr); +BENCHMARK(BM_SmallObjectCreationWithSharedPtr_Pooled); +BENCHMARK(BM_SmallObjectCreationWithAny); +BENCHMARK(BM_LargeObjectCreationWithProxy); +BENCHMARK(BM_LargeObjectCreationWithProxy_Pooled); +BENCHMARK(BM_LargeObjectCreationWithProxy_Shared); +BENCHMARK(BM_LargeObjectCreationWithProxy_SharedPooled); +BENCHMARK(BM_LargeObjectCreationWithUniquePtr); +BENCHMARK(BM_LargeObjectCreationWithSharedPtr); +BENCHMARK(BM_LargeObjectCreationWithSharedPtr_Pooled); +BENCHMARK(BM_LargeObjectCreationWithAny); } // namespace diff --git a/benchmarks/proxy_invocation_benchmark.cpp b/benchmarks/proxy_invocation_benchmark.cpp deleted file mode 100644 index b19aff3..0000000 --- a/benchmarks/proxy_invocation_benchmark.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include "proxy_invocation_benchmark_context.h" - -namespace { - -void BM_SmallObjectInvocationViaProxy(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaProxy_Shared(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData_Shared(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaProxyView(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationProxyTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaVirtualFunction(benchmark::State& state) { - auto data = GenerateSmallObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_SmallObjectInvocationViaVirtualFunction_Shared( - benchmark::State& state) { - auto data = GenerateSmallObjectInvocationVirtualFunctionTestData_Shared(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxy(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxy_Shared(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData_Shared(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaProxyView(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationProxyTestData(); - std::vector> views(data.begin(), - data.end()); - for (auto _ : state) { - for (auto& p : views) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) { - auto data = GenerateLargeObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -void BM_LargeObjectInvocationViaVirtualFunction_Shared( - benchmark::State& state) { - auto data = GenerateLargeObjectInvocationVirtualFunctionTestData(); - for (auto _ : state) { - for (auto& p : data) { - int result = p->Fun(); - benchmark::DoNotOptimize(result); - } - } -} - -BENCHMARK(BM_SmallObjectInvocationViaProxy); -BENCHMARK(BM_SmallObjectInvocationViaProxy_Shared); -BENCHMARK(BM_SmallObjectInvocationViaProxyView); -BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction); -BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_Shared); -BENCHMARK(BM_LargeObjectInvocationViaProxy); -BENCHMARK(BM_LargeObjectInvocationViaProxy_Shared); -BENCHMARK(BM_LargeObjectInvocationViaProxyView); -BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction); -BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_Shared); - -} // namespace diff --git a/benchmarks/proxy_invocation_benchmark_context.h b/benchmarks/proxy_invocation_benchmark_context.h deleted file mode 100644 index 5404330..0000000 --- a/benchmarks/proxy_invocation_benchmark_context.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include - -PRO_DEF_MEM_DISPATCH(MemFun, Fun); - -struct InvocationTestFacade : pro::facade_builder // - ::add_convention // - ::add_skill // - ::build {}; - -struct InvocationTestBase { - virtual int Fun() const = 0; - virtual ~InvocationTestBase() = default; -}; - -std::vector> - GenerateSmallObjectInvocationProxyTestData(); -std::vector> - GenerateSmallObjectInvocationProxyTestData_Shared(); -std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData(); -std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData_Shared(); -std::vector> - GenerateLargeObjectInvocationProxyTestData(); -std::vector> - GenerateLargeObjectInvocationProxyTestData_Shared(); -std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData(); -std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData_Shared(); diff --git a/benchmarks/proxy_operation_benchmark.cpp b/benchmarks/proxy_operation_benchmark.cpp new file mode 100644 index 0000000..978d58f --- /dev/null +++ b/benchmarks/proxy_operation_benchmark.cpp @@ -0,0 +1,267 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include + +#include "proxy_operation_benchmark_context.h" + +namespace { + +void BM_SmallObjectInvocationViaProxy(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaProxy_Shared(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaProxyView(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + std::vector> views(data.begin(), + data.end()); + for (auto _ : state) { + for (auto& p : views) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction(benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction_Shared( + benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectInvocationViaVirtualFunction_RawPtr( + benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + std::vector ptrs; + ptrs.reserve(data.size()); + for (auto& p : data) { + ptrs.push_back(p.get()); + } + for (auto _ : state) { + for (auto& p : ptrs) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxy(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxy_Shared(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData_Shared(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaProxyView(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + std::vector> views(data.begin(), + data.end()); + for (auto _ : state) { + for (auto& p : views) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction_Shared( + benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + for (auto _ : state) { + for (auto& p : data) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_LargeObjectInvocationViaVirtualFunction_RawPtr( + benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + std::vector ptrs; + ptrs.reserve(data.size()); + for (auto& p : data) { + ptrs.push_back(p.get()); + } + for (auto _ : state) { + for (auto& p : ptrs) { + int result = p->Fun(); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_SmallObjectRelocationViaProxy(benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaProxy_NothrowRelocatable( + benchmark::State& state) { + auto data = GenerateSmallObjectProxyTestData_NothrowRelocatable(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaUniquePtr(benchmark::State& state) { + auto data = GenerateSmallObjectVirtualFunctionTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_SmallObjectRelocationViaAny(benchmark::State& state) { + auto data = GenerateSmallObjectAnyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaProxy(benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaProxy_NothrowRelocatable( + benchmark::State& state) { + auto data = GenerateLargeObjectProxyTestData_NothrowRelocatable(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaUniquePtr(benchmark::State& state) { + auto data = GenerateLargeObjectVirtualFunctionTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +void BM_LargeObjectRelocationViaAny(benchmark::State& state) { + auto data = GenerateLargeObjectAnyTestData(); + decltype(data) buffer(data.size()); + for (auto _ : state) { + for (std::size_t i = 0; i < data.size(); ++i) { + buffer[i] = std::move(data[i]); + } + benchmark::DoNotOptimize(buffer); + std::swap(data, buffer); + } +} + +BENCHMARK(BM_SmallObjectInvocationViaProxy); +BENCHMARK(BM_SmallObjectInvocationViaProxy_Shared); +BENCHMARK(BM_SmallObjectInvocationViaProxyView); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_Shared); +BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction_RawPtr); +BENCHMARK(BM_LargeObjectInvocationViaProxy); +BENCHMARK(BM_LargeObjectInvocationViaProxy_Shared); +BENCHMARK(BM_LargeObjectInvocationViaProxyView); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_Shared); +BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction_RawPtr); +BENCHMARK(BM_SmallObjectRelocationViaProxy); +BENCHMARK(BM_SmallObjectRelocationViaProxy_NothrowRelocatable); +BENCHMARK(BM_SmallObjectRelocationViaUniquePtr); +BENCHMARK(BM_SmallObjectRelocationViaAny); +BENCHMARK(BM_LargeObjectRelocationViaProxy); +BENCHMARK(BM_LargeObjectRelocationViaProxy_NothrowRelocatable); +BENCHMARK(BM_LargeObjectRelocationViaUniquePtr); +BENCHMARK(BM_LargeObjectRelocationViaAny); + +} // namespace diff --git a/benchmarks/proxy_invocation_benchmark_context.cpp b/benchmarks/proxy_operation_benchmark_context.cpp similarity index 72% rename from benchmarks/proxy_invocation_benchmark_context.cpp rename to benchmarks/proxy_operation_benchmark_context.cpp index 13395af..d0523a1 100644 --- a/benchmarks/proxy_invocation_benchmark_context.cpp +++ b/benchmarks/proxy_operation_benchmark_context.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#include "proxy_invocation_benchmark_context.h" +#include "proxy_operation_benchmark_context.h" namespace { @@ -77,15 +77,23 @@ auto GenerateTestData(const F& generator) { } // namespace std::vector> - GenerateSmallObjectInvocationProxyTestData() { + GenerateSmallObjectProxyTestData() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy>(seed); }); } +std::vector> + GenerateSmallObjectProxyTestData_NothrowRelocatable() { + return GenerateTestData( + [](IntConstant, int seed) { + return pro::make_proxy>(seed); + }); +} std::vector> - GenerateSmallObjectInvocationProxyTestData_Shared() { + GenerateSmallObjectProxyTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy_shared> }); } std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData() { + GenerateSmallObjectVirtualFunctionTestData() { return GenerateTestData( [](IntConstant, int seed) { return std::unique_ptr{ @@ -101,23 +109,38 @@ std::vector> }); } std::vector> - GenerateSmallObjectInvocationVirtualFunctionTestData_Shared() { + GenerateSmallObjectVirtualFunctionTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return std::shared_ptr{ std::make_shared>(seed)}; }); } +std::vector GenerateSmallObjectAnyTestData() { + return GenerateTestData( + [](IntConstant, int seed) { + return std::make_any>(seed); + }); +} + std::vector> - GenerateLargeObjectInvocationProxyTestData() { + GenerateLargeObjectProxyTestData() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy>(seed); }); } +std::vector> + GenerateLargeObjectProxyTestData_NothrowRelocatable() { + return GenerateTestData( + [](IntConstant, int seed) { + return pro::make_proxy>(seed); + }); +} std::vector> - GenerateLargeObjectInvocationProxyTestData_Shared() { + GenerateLargeObjectProxyTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return pro::make_proxy_shared> }); } std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData() { + GenerateLargeObjectVirtualFunctionTestData() { return GenerateTestData( [](IntConstant, int seed) { return std::unique_ptr{ @@ -133,10 +156,16 @@ std::vector> }); } std::vector> - GenerateLargeObjectInvocationVirtualFunctionTestData_Shared() { + GenerateLargeObjectVirtualFunctionTestData_Shared() { return GenerateTestData( [](IntConstant, int seed) { return std::shared_ptr{ std::make_shared>(seed)}; }); } +std::vector GenerateLargeObjectAnyTestData() { + return GenerateTestData( + [](IntConstant, int seed) { + return std::make_any>(seed); + }); +} diff --git a/benchmarks/proxy_operation_benchmark_context.h b/benchmarks/proxy_operation_benchmark_context.h new file mode 100644 index 0000000..27a3346 --- /dev/null +++ b/benchmarks/proxy_operation_benchmark_context.h @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include + +#include + +PRO_DEF_MEM_DISPATCH(MemFun, Fun); + +struct InvocationTestFacade : pro::facade_builder // + ::add_convention // + ::add_skill // + ::add_skill // + ::build {}; + +struct NothrowRelocatableInvocationTestFacade : InvocationTestFacade { + static constexpr auto relocatability = pro::constraint_level::nothrow; +}; + +struct InvocationTestBase { + virtual int Fun() const = 0; + virtual ~InvocationTestBase() = default; +}; + +std::vector> + GenerateSmallObjectProxyTestData(); +std::vector> + GenerateSmallObjectProxyTestData_NothrowRelocatable(); +std::vector> + GenerateSmallObjectProxyTestData_Shared(); +std::vector> + GenerateSmallObjectVirtualFunctionTestData(); +std::vector> + GenerateSmallObjectVirtualFunctionTestData_Shared(); +std::vector GenerateSmallObjectAnyTestData(); + +std::vector> + GenerateLargeObjectProxyTestData(); +std::vector> + GenerateLargeObjectProxyTestData_NothrowRelocatable(); +std::vector> + GenerateLargeObjectProxyTestData_Shared(); +std::vector> + GenerateLargeObjectVirtualFunctionTestData(); +std::vector> + GenerateLargeObjectVirtualFunctionTestData_Shared(); +std::vector GenerateLargeObjectAnyTestData(); diff --git a/tools/report_generator/main.cpp b/tools/report_generator/main.cpp index af2820a..3a0043a 100644 --- a/tools/report_generator/main.cpp +++ b/tools/report_generator/main.cpp @@ -6,8 +6,8 @@ #include #include #include -#include #include +#include #include #include @@ -76,12 +76,32 @@ struct Metric { } }; +struct MetricGroup { + std::string Name; + std::string Description; + std::vector Metrics; + + friend void to_json(nlohmann::json& j, const MetricGroup& m) { + j = nlohmann::json{ + {"Name", m.Name}, + {"Description", m.Description}, + {"Metrics", m.Metrics}, + }; + } + + friend void from_json(const nlohmann::json& j, MetricGroup& m) { + j.at("Name").get_to(m.Name); + j.at("Description").get_to(m.Description); + j.at("Metrics").get_to(m.Metrics); + } +}; + struct ReportConfig { std::string TargetName; double YellowIndicatorThreshold; std::string OutputPath; std::vector Environments; - std::vector Metrics; + std::vector MetricGroups; friend void to_json(nlohmann::json& j, const ReportConfig& rc) { j = nlohmann::json{ @@ -89,7 +109,7 @@ struct ReportConfig { {"YellowIndicatorThreshold", rc.YellowIndicatorThreshold}, {"OutputPath", rc.OutputPath}, {"Environments", rc.Environments}, - {"Metrics", rc.Metrics}, + {"MetricGroups", rc.MetricGroups}, }; } @@ -98,7 +118,7 @@ struct ReportConfig { j.at("YellowIndicatorThreshold").get_to(rc.YellowIndicatorThreshold); j.at("OutputPath").get_to(rc.OutputPath); j.at("Environments").get_to(rc.Environments); - j.at("Metrics").get_to(rc.Metrics); + j.at("MetricGroups").get_to(rc.MetricGroups); } }; @@ -113,7 +133,8 @@ EnvironmentInfo ParseEnvironmentInfo(const std::filesystem::path& file) { return obj.get(); } -std::unordered_map ParseBenchmarkingResults(const std::filesystem::path& file) { +std::unordered_map + ParseBenchmarkingResults(const std::filesystem::path& file) { nlohmann::json obj; { std::ifstream in; @@ -146,56 +167,66 @@ void GenerateReport(const std::filesystem::path& config_path) { std::vector> benchmarks; benchmarks.reserve(config.Environments.size()); for (auto& environment : config.Environments) { - benchmarks.push_back(ParseBenchmarkingResults(environment.BenchmarkingResultsPath)); + benchmarks.push_back( + ParseBenchmarkingResults(environment.BenchmarkingResultsPath)); } std::ofstream out; out.exceptions(std::ios_base::failbit | std::ios_base::badbit); - out.open(config.OutputPath, std::ios_base::out | std::ios_base::app | std::ios_base::binary); - out << "| |"; - for (auto& environment : config.Environments) { - out << " " << environment.Description << " |"; - } - out << "\n"; - out << "| - |"; - for (std::size_t i = 0; i < config.Environments.size(); ++i) { - out << " - |"; - } - out << "\n"; - for (auto& metric : config.Metrics) { - out << "| " << metric.Name << " |"; - for (auto& benchmark : benchmarks) { - double target = benchmark.at(metric.TargetBenchmarkName); - double baseline = benchmark.at(metric.BaselineBenchmarkName); - double rate = (baseline - target) * 100 / target; - bool is_negative = rate < 0; - if (is_negative) { - rate = -rate; - } - out << " "; - if (rate < config.YellowIndicatorThreshold) { - out << "\xf0\x9f\x9f\xa1"; // Yellow circle - } else if (is_negative) { - out << "\xf0\x9f\x94\xb4"; // Red circle - } else { - out << "\xf0\x9f\x9f\xa2"; // Green circle - } - auto rate_str = std::format("{:.1f}", rate); - std::string message; - if (rate_str == "0.0") { - out << config.TargetName << " has similar performance"; - } else { - out << config.TargetName << " is about **" << rate_str << "% " << (is_negative ? "slower" : "faster") << "**"; + out.open(config.OutputPath, + std::ios_base::out | std::ios_base::app | std::ios_base::binary); + for (auto& metric_group : config.MetricGroups) { + out << "## " << metric_group.Name << "\n\n"; + out << metric_group.Description << "\n\n"; + out << "| |"; + for (auto& environment : config.Environments) { + out << " " << environment.Description << " |"; + } + out << "\n"; + out << "| - |"; + for (std::size_t i = 0; i < config.Environments.size(); ++i) { + out << " - |"; + } + out << "\n"; + for (auto& metric : metric_group.Metrics) { + out << "| " << metric.Name << " |"; + for (auto& benchmark : benchmarks) { + double target = benchmark.at(metric.TargetBenchmarkName); + double baseline = benchmark.at(metric.BaselineBenchmarkName); + double rate = (baseline - target) * 100 / target; + bool is_negative = rate < 0; + if (is_negative) { + rate = -rate; + } + out << " "; + if (rate < config.YellowIndicatorThreshold) { + out << "\xf0\x9f\x9f\xa1"; // Yellow circle + } else if (is_negative) { + out << "\xf0\x9f\x94\xb4"; // Red circle + } else { + out << "\xf0\x9f\x9f\xa2"; // Green circle + } + auto rate_str = std::format("{:.1f}", rate); + std::string message; + if (rate_str == "0.0") { + out << config.TargetName << " has similar performance"; + } else { + out << config.TargetName << " is about **" << rate_str << "% " + << (is_negative ? "slower" : "faster") << "**"; + } + out << " |"; } - out << " |"; + out << "\n"; } out << "\n"; } - out << "\n## Environments\n\n"; + out << "## Environments\n\n"; out << "| | Operating System | Kernel Version | Architecture | Compiler |\n"; out << "| - | - | - | - | - |\n"; for (auto& environment : config.Environments) { EnvironmentInfo env_info = ParseEnvironmentInfo(environment.InfoPath); - out << "| **" << environment.Description << "** | " << env_info.OS << " | " << env_info.KernelVersion << " | " << env_info.Architecture << " | " << env_info.Compiler << " |\n"; + out << "| **" << environment.Description << "** | " << env_info.OS << " | " + << env_info.KernelVersion << " | " << env_info.Architecture << " | " + << env_info.Compiler << " |\n"; } } diff --git a/tools/report_generator/report-config.json b/tools/report_generator/report-config.json index f0da16a..adcb22f 100644 --- a/tools/report_generator/report-config.json +++ b/tools/report_generator/report-config.json @@ -34,106 +34,154 @@ "BenchmarkingResultsPath": "artifacts/drop-oneapi/benchmarking-results.json" } ], - "Metrics": [ - { - "Name": "Indirect invocation on small objects via `proxy` vs. virtual functions", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on small objects via `proxy_view` vs. virtual functions", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxyView", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on small objects via `proxy` vs. virtual functions (shared ownership)", - "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy_Shared", - "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_Shared" - }, - { - "Name": "Indirect invocation on large objects via `proxy` vs. virtual functions", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on large objects via `proxy_view` vs. virtual functions", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxyView", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" - }, - { - "Name": "Indirect invocation on large objects via `proxy` vs. virtual functions (shared ownership)", - "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy_Shared", - "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_Shared" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (without memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (with memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy_Shared", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy_SharedPooled", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for small objects with `proxy` vs. `std::any`", - "TargetBenchmarkName": "BM_SmallObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_SmallObjectManagementWithAny" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (without memory pool) vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (with memory pool) vs. `std::unique_ptr`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithUniquePtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Shared", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_SharedPooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithSharedPtr_Pooled" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (without memory pool) vs. `std::any`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithAny" - }, - { - "Name": "Basic lifetime management for large objects with `proxy` (with memory pool) vs. `std::any`", - "TargetBenchmarkName": "BM_LargeObjectManagementWithProxy_Pooled", - "BaselineBenchmarkName": "BM_LargeObjectManagementWithAny" + "MetricGroups": [ + { + "Name": "Core Metrics", + "Description": "These benchmarks define the library's primary success criteria. They should generally reflect positive outcomes and remain stable as the library evolves, with regressions carefully monitored and addressed.", + "Metrics": [ + { + "Name": "Indirect invocation on small objects via `proxy` vs. `std::unique_ptr` + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction" + }, + { + "Name": "Indirect invocation on small objects via `proxy_view` vs. raw pointer + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxyView", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_RawPtr" + }, + { + "Name": "Indirect invocation on small objects via `proxy` (shared ownership) vs. `std::shared_ptr` + virtual functions", + "TargetBenchmarkName": "BM_SmallObjectInvocationViaProxy_Shared", + "BaselineBenchmarkName": "BM_SmallObjectInvocationViaVirtualFunction_Shared" + }, + { + "Name": "Indirect invocation on large objects via `proxy` vs. `std::unique_ptr` + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction" + }, + { + "Name": "Indirect invocation on large objects via `proxy_view` vs. raw pointer + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxyView", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_RawPtr" + }, + { + "Name": "Indirect invocation on large objects via `proxy` (shared ownership) vs. `std::shared_ptr` + virtual functions", + "TargetBenchmarkName": "BM_LargeObjectInvocationViaProxy_Shared", + "BaselineBenchmarkName": "BM_LargeObjectInvocationViaVirtualFunction_Shared" + }, + { + "Name": "Creation for small objects with `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (with memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for large objects with `proxy` (with memory pool) vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_SharedPooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr_Pooled" + } + ] + }, + { + "Name": "Comparative Metrics", + "Description": "These benchmarks provide perspective by comparison with other well-known implementations. They are not required to consistently show positive results, but instead highlight relative strengths, trade-offs, and areas of differentiation.", + "Metrics": [ + { + "Name": "Creation for small objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (without memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy_Shared", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for small objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy_SharedPooled", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for small objects with `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_SmallObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_SmallObjectCreationWithAny" + }, + { + "Name": "Creation for large objects with `proxy` (without memory pool) vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithUniquePtr" + }, + { + "Name": "Creation for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for large objects with `proxy` (exclusive ownership) vs. `std::shared_ptr` (both with memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr_Pooled" + }, + { + "Name": "Creation for large objects with `proxy` (shared ownership) vs. `std::shared_ptr` (both without memory pool)", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Shared", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithSharedPtr" + }, + { + "Name": "Creation for large objects with `proxy` (without memory pool) vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithAny" + }, + { + "Name": "Creation for large objects with `proxy` (with memory pool) vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectCreationWithProxy_Pooled", + "BaselineBenchmarkName": "BM_LargeObjectCreationWithAny" + } + ] + }, + { + "Name": "Observational Metrics", + "Description": "These benchmarks are included for completeness and transparency. While they may not represent target outcomes, they offer valuable context for users to understand the broader characteristics of the library.", + "Metrics": [ + { + "Name": "Relocation on small objects via `proxy` (trivially-relocatable by default) vs. `proxy` (nothrow-relocatable)", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaProxy_NothrowRelocatable" + }, + { + "Name": "Relocation on small objects via `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaUniquePtr" + }, + { + "Name": "Relocation on small objects via `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_SmallObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_SmallObjectRelocationViaAny" + }, + { + "Name": "Relocation on large objects via `proxy` (trivially-relocatable by default) vs. `proxy` (nothrow-relocatable)", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaProxy_NothrowRelocatable" + }, + { + "Name": "Relocation on large objects via `proxy` vs. `std::unique_ptr`", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaUniquePtr" + }, + { + "Name": "Relocation on large objects via `proxy` vs. `std::any`", + "TargetBenchmarkName": "BM_LargeObjectRelocationViaProxy", + "BaselineBenchmarkName": "BM_LargeObjectRelocationViaAny" + } + ] } ] } \ No newline at end of file From 1d4ea35f45922ead34f90e26a44fd0d55521b803 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Sat, 6 Dec 2025 20:28:53 +0800 Subject: [PATCH 08/14] Update toolchain (#378) --- .github/workflows/bvt-clang.yml | 14 ++++++------- .github/workflows/bvt-compatibility.yml | 9 +++++++++ .github/workflows/bvt-gcc.yml | 14 +++++++------ .github/workflows/bvt-nvhpc.yml | 8 ++++---- CMakeLists.txt | 16 +++++++++++++++ benchmarks/CMakeLists.txt | 11 ++--------- cmake/msft_proxy4ModuleTargets.cmake | 1 + docs/CMakeLists.txt | 7 ------- include/proxy/v4/proxy.h | 2 +- include/proxy/v4/proxy_fmt.h | 26 ++++++++++--------------- tests/CMakeLists.txt | 18 ++--------------- tests/proxy_fmt_format_tests.cpp | 11 ----------- tools/report_generator/CMakeLists.txt | 4 ++-- 13 files changed, 62 insertions(+), 79 deletions(-) diff --git a/.github/workflows/bvt-clang.yml b/.github/workflows/bvt-clang.yml index 84c6c2c..4c62cb4 100644 --- a/.github/workflows/bvt-clang.yml +++ b/.github/workflows/bvt-clang.yml @@ -11,24 +11,24 @@ jobs: run: | wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh - sudo ./llvm.sh 20 - sudo apt install -y libc++-20-dev clang-format-20 + sudo ./llvm.sh 21 + sudo apt install -y libc++-21-dev clang-format-21 - name: check compiler version run: | - clang++-20 --version + clang++-21 --version - - name: build and run test with clang 20 + - name: build and run test with clang 21 run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-21 -DCMAKE_CXX_COMPILER=clang++-21 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) echo "Running clang-format on ${#FILES[@]} files: ${FILES[*]}" - clang-format-20 --dry-run --Werror "${FILES[@]}" + clang-format-21 --dry-run --Werror "${FILES[@]}" cmake --build build -j ctest --test-dir build -j mkdir build/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++-20 build/drop/env-info.json + ./tools/dump_build_env.sh clang++-21 build/drop/env-info.json - name: run benchmarks run: | diff --git a/.github/workflows/bvt-compatibility.yml b/.github/workflows/bvt-compatibility.yml index 7164c83..83f41df 100644 --- a/.github/workflows/bvt-compatibility.yml +++ b/.github/workflows/bvt-compatibility.yml @@ -9,6 +9,9 @@ jobs: - name: install compilers run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 20 sudo apt install -y gcc-13 g++-13 gcc-14 g++-14 clang-16 clang-17 clang-18 clang-19 libc++-17-dev - name: check compiler versions @@ -19,6 +22,7 @@ jobs: clang++-17 --version clang++-18 --version clang++-19 --version + clang++-20 --version - name: build and run test with gcc 14 run: | @@ -32,6 +36,11 @@ jobs: cmake --build build-gcc-13 -j ctest --test-dir build-gcc-13 -j + - name: build and run test with clang 20 + run: | + cmake -B build-clang-20 -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake --build build-clang-20 -j + ctest --test-dir build-clang-20 -j - name: build and run test with clang 19 run: | cmake -B build-clang-19 -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE diff --git a/.github/workflows/bvt-gcc.yml b/.github/workflows/bvt-gcc.yml index 81679dd..b0909e2 100644 --- a/.github/workflows/bvt-gcc.yml +++ b/.github/workflows/bvt-gcc.yml @@ -4,25 +4,27 @@ on: jobs: bvt-gcc: runs-on: ubuntu-24.04 + container: gcc:15 steps: - uses: actions/checkout@v4 - - name: install gcc + - name: install dependencies run: | - sudo apt install -y gcc-14 g++-14 + apt-get update + apt-get install -y cmake ninja-build - name: check compiler version run: | - g++-14 --version + g++ --version - - name: build and run test with gcc 14 + - name: build and run test with gcc 15 run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake -B build -GNinja -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE cmake --build build -j ctest --test-dir build -j mkdir build/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh g++-14 build/drop/env-info.json + ./tools/dump_build_env.sh g++ build/drop/env-info.json - name: run benchmarks run: | diff --git a/.github/workflows/bvt-nvhpc.yml b/.github/workflows/bvt-nvhpc.yml index 78696e4..9a33331 100644 --- a/.github/workflows/bvt-nvhpc.yml +++ b/.github/workflows/bvt-nvhpc.yml @@ -10,16 +10,16 @@ jobs: - name: free disk space uses: jlumbroso/free-disk-space@v1.3.1 - - name: install NVHPC 25.7 + - name: install NVHPC 25.11 run: | curl https://developer.download.nvidia.com/hpc-sdk/ubuntu/DEB-GPG-KEY-NVIDIA-HPC-SDK | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg echo 'deb [signed-by=/usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | sudo tee /etc/apt/sources.list.d/nvhpc.list sudo apt-get update -y - sudo apt-get install -y nvhpc-25-7 + sudo apt-get install -y nvhpc-25-11 - - name: build and run test with NVHPC 25.7 + - name: build and run test with NVHPC 25.11 run: | - PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/25.7/compilers/bin:$PATH; export PATH + PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin:$PATH; export PATH cmake -B build -GNinja -DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++ -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE cmake --build build -j ctest --test-dir build -j diff --git a/CMakeLists.txt b/CMakeLists.txt index ee8d16c..ae1ffdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,22 @@ install( # build tests if BUILD_TESTING is ON if (BUILD_TESTING) include(CTest) + + include(FetchContent) + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects URL changes. + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + endif() + + FetchContent_Declare( + fmt + URL https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz + URL_HASH SHA256=ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea + SYSTEM + ) + FetchContent_MakeAvailable(fmt) + add_subdirectory(tests) add_subdirectory(benchmarks) add_subdirectory(docs) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 882f104..018a984 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -1,16 +1,9 @@ project(msft_proxy_benchmarks) -include(FetchContent) -# The policy uses the download time for timestamp, instead of the timestamp in the archive. This -# allows for proper rebuilds when a projects URL changes. -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - FetchContent_Declare( benchmark - URL https://github.com/google/benchmark/archive/refs/tags/v1.9.0.tar.gz - URL_HASH SHA256=35a77f46cc782b16fac8d3b107fbfbb37dcd645f7c28eee19f3b8e0758b48994 + URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.tar.gz + URL_HASH SHA256=b334658edd35efcf06a99d9be21e4e93e092bd5f95074c1673d5c8705d95c104 ) set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Disable tests for google benchmark") set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "Disable google benchmark unit tests") diff --git a/cmake/msft_proxy4ModuleTargets.cmake b/cmake/msft_proxy4ModuleTargets.cmake index a4c2d1b..ee13002 100644 --- a/cmake/msft_proxy4ModuleTargets.cmake +++ b/cmake/msft_proxy4ModuleTargets.cmake @@ -22,5 +22,6 @@ target_sources(msft_proxy4_module PUBLIC target_compile_features(msft_proxy4_module PUBLIC cxx_std_20) target_compile_options(msft_proxy4_module PRIVATE $<$:/utf-8> + $<$:-Wno-c++2b-extensions> ) target_link_libraries(msft_proxy4_module PUBLIC msft_proxy4::proxy) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 4ed3bb1..ff9dc5b 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -1,12 +1,5 @@ project(msft_proxy_docs) -FetchContent_Declare( - fmt - URL https://github.com/fmtlib/fmt/archive/refs/tags/11.2.0.tar.gz - URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e -) -FetchContent_MakeAvailable(fmt) - find_package(Python3 REQUIRED COMPONENTS Interpreter) file(GLOB_RECURSE DOC_FILES "*.md") diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index e502b17..923e41c 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -2456,7 +2456,7 @@ struct operator_dispatch; #define PROD_DEF_RHS_ASSIGNMENT_OP_ACCESSOR(oq, pq, ne, ...) \ template \ struct accessor { \ - friend Arg& operator __VA_ARGS__(Arg & arg, P pq self) ne { \ + friend Arg& operator __VA_ARGS__(Arg& arg, P pq self) ne { \ proxy_invoke(static_cast

(self), arg); \ return arg; \ } \ diff --git a/include/proxy/v4/proxy_fmt.h b/include/proxy/v4/proxy_fmt.h index 7d2c483..be6a368 100644 --- a/include/proxy/v4/proxy_fmt.h +++ b/include/proxy/v4/proxy_fmt.h @@ -11,31 +11,25 @@ #error Please ensure that proxy.h is included before proxy_fmt.h. #endif // __msft_lib_proxy4 -#if FMT_VERSION >= 60100 -static_assert(fmt::is_char::value, - "The {fmt} library must have wchar_t support enabled. " - "Include fmt/xchar.h before including proxy_fmt.h."); -#else +#if FMT_VERSION < 60100 #error Please ensure that the appropriate {fmt} headers (version 6.1.0 or \ later) are included before proxy_fmt.h. -#endif // FMT_VERSION >= 60100 +#endif // FMT_VERSION < 60100 namespace pro::inline v4 { namespace details { template -struct fmt_format_overload_traits; -template <> -struct fmt_format_overload_traits - : std::type_identity {}; -template <> -struct fmt_format_overload_traits - : std::type_identity {}; +#if FMT_VERSION >= 110000 +using fmt_buffered_context = fmt::buffered_context; +#else +using fmt_buffered_context = fmt::buffer_context; +#endif // FMT_VERSION + template -using fmt_format_overload_t = typename fmt_format_overload_traits::type; +using fmt_format_overload_t = fmt_buffered_context::iterator( + std::basic_string_view spec, fmt_buffered_context& fc) const; struct fmt_format_dispatch { template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 775e958..127c21b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,29 +1,15 @@ project(msft_proxy_tests) -include(FetchContent) -# The policy uses the download time for timestamp, instead of the timestamp in the archive. This -# allows for proper rebuilds when a projects URL changes. -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - FetchContent_Declare( googletest - URL https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz - URL_HASH SHA256=7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926 + URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz + URL_HASH SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # For Windows: Prevent overriding the parent project's compiler/linker settings set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # Disable GMock FetchContent_MakeAvailable(googletest) include(GoogleTest) -FetchContent_Declare( - fmt - URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz - URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e -) -FetchContent_MakeAvailable(fmt) - add_executable(msft_proxy_tests proxy_creation_tests.cpp proxy_dispatch_tests.cpp diff --git a/tests/proxy_fmt_format_tests.cpp b/tests/proxy_fmt_format_tests.cpp index 74293f9..10e1fb8 100644 --- a/tests/proxy_fmt_format_tests.cpp +++ b/tests/proxy_fmt_format_tests.cpp @@ -3,19 +3,8 @@ #include -#if defined(__NVCOMPILER) -#pragma diagnostic push -#pragma diag_suppress inline_gnu_noinline_conflict -#pragma diagnostic push -#pragma diag_suppress code_is_unreachable -#endif // defined(__NVCOMPILER) #include #include -#if defined(__NVCOMPILER) -#pragma diagnostic pop -#pragma diagnostic pop -#endif // defined(__NVCOMPILER) - #include #include diff --git a/tools/report_generator/CMakeLists.txt b/tools/report_generator/CMakeLists.txt index bd99f38..63eda32 100644 --- a/tools/report_generator/CMakeLists.txt +++ b/tools/report_generator/CMakeLists.txt @@ -11,8 +11,8 @@ endif() FetchContent_Declare( nlohmann_json - URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz - URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d + URL https://github.com/nlohmann/json/archive/refs/tags/v3.12.0.tar.gz + URL_HASH SHA256=4b92eb0c06d10683f7447ce9406cb97cd4b453be18d7279320f7b2f025c10187 ) FetchContent_MakeAvailable(nlohmann_json) From a56029743f3058d9702f5a127a0f50350daa6d91 Mon Sep 17 00:00:00 2001 From: Zephyr Lykos Date: Wed, 28 Jan 2026 16:10:30 +0800 Subject: [PATCH 09/14] build: support meson build system (#375) * tests: migrate deprecated std::is_trivial_v * tools: add python typings * build: support meson build system CMake feature parity: - [x] Export meson target - [x] Export meson c++20 modules target - [ ] Export cmake target - [x] Installing headers - [x] Googletest support - [x] Freestanding tests - [x] Modules tests - [x] Google Benchmark support - [x] Extract examples from docs Extra features: - [x] Export pkgconfig target - [x] clang-format support - [x] Make fmtlib optional when building examples - [x] Install documents to docdir Tested on Fedora Linux 44 (gcc/clang/msvc-wine + ninja) and Windows 10 22H2 (msvc + vs2022). * ci: fix shellcheck warnings * ci: add meson tests * style: run clang-format * ci: workaround clang sanitizer linking bug * build/meson: fix modules builds on mingw winpthreads * build/meson: do not link -nodefaultlibs on sanitized builds * build/meson: increase max object section count on msvc * ci: only test release builds, do not run benchmarks * ci: pin runner versions * ci: exclude unsupported / broken platforms * build: add a notice about clang-cl broken msvc compatibility * build: meson gtest autodiscovery * build: meson doctest autoglob * ci: manage meson builds from proxy-ci * build: meson benchmarks autogen * build: make meson use common doctest extract logic * build/meson: auto disable tests if in a subproject * build/meson: auto disable benchmarks if in a subproject * build/meson: remove docdir installation the glob only contains files with C++ source code now. * build/meson: remove all autogen * build/meson: only add WINPTHREAD_COND_DECL workaround if winpthreads is found * ci: reword step names * ci: move meson ci tests into bvt workflows * build/meson: support pgi compilers * ci: remove meson from compatibility tests * ci: run meson and cmake in one pass * Test for std::format using __cpp_lib_format With specialization for libc++. Fixes compilation on libc++ 16-18 that has incomplete std::format support. * build/meson: remove module support too many compiler-specific workarounds. waiting for better upstream support. * fixup! ci: add meson tests * fixup! Test for std::format using __cpp_lib_format * build/meson: add test and benchmarks to all targets, only test build benchmark on ci * fixup! Test for std::format using __cpp_lib_format * fixup! build/meson: add test and benchmarks to all targets, only test build benchmark on ci * fixup! build/meson: add test and benchmarks to all targets, only test build benchmark on ci * build/meson: update fmt to 12.1.0 * fixup! tools: add python typings * fixup! build/meson: remove all autogen --- .github/workflows/bvt-appleclang.yml | 29 +++--- .github/workflows/bvt-clang.yml | 42 +++++---- .github/workflows/bvt-compatibility.yml | 83 +++++++---------- .github/workflows/bvt-gcc.yml | 27 +++--- .github/workflows/bvt-msvc.yml | 26 ++++-- .github/workflows/bvt-nvhpc.yml | 32 +++++-- .github/workflows/bvt-oneapi.yml | 40 +++++--- .github/workflows/pipeline-release.yml | 4 +- .gitignore | 7 ++ benchmarks/meson.build | 13 +++ docs/meson.build | 39 ++++++++ docs/spec/proxy_view.md | 1 + include/proxy/v4/proxy.h | 16 ++-- include/proxy/v4/proxy.ixx | 8 +- meson.build | 118 ++++++++++++++++++++++++ meson.format | 4 + meson.options | 18 ++++ subprojects/fmt.wrap | 13 +++ subprojects/google-benchmark.wrap | 13 +++ subprojects/gtest.wrap | 16 ++++ tests/meson.build | 67 ++++++++++++++ tests/proxy_dispatch_tests.cpp | 4 + tests/proxy_format_tests.cpp | 10 ++ tests/proxy_reflection_tests.cpp | 3 +- tools/extract_example_code_from_docs.py | 87 ++++++++++++----- tools/meson_extract_doctest.py | 25 +++++ 26 files changed, 587 insertions(+), 158 deletions(-) create mode 100644 benchmarks/meson.build create mode 100644 docs/meson.build create mode 100644 meson.build create mode 100644 meson.format create mode 100644 meson.options create mode 100644 subprojects/fmt.wrap create mode 100644 subprojects/google-benchmark.wrap create mode 100644 subprojects/gtest.wrap create mode 100644 tests/meson.build create mode 100644 tools/meson_extract_doctest.py diff --git a/.github/workflows/bvt-appleclang.yml b/.github/workflows/bvt-appleclang.yml index 32f84f1..ace5880 100644 --- a/.github/workflows/bvt-appleclang.yml +++ b/.github/workflows/bvt-appleclang.yml @@ -7,26 +7,33 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: check compiler versions run: | - clang --version + cc --version xcodebuild -version - - name: build and run test with AppleClang + - name: build and run test with AppleClang on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++ build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh c++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with AppleClang on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-appleclang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-clang.yml b/.github/workflows/bvt-clang.yml index 4c62cb4..1706930 100644 --- a/.github/workflows/bvt-clang.yml +++ b/.github/workflows/bvt-clang.yml @@ -7,35 +7,45 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install clang run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 21 - sudo apt install -y libc++-21-dev clang-format-21 + curl https://apt.llvm.org/llvm.sh | sudo bash -s -- 21 + sudo apt install -y clang-21 libc++-21-dev clang-format-21 + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-21 + CXX=clang++-21 + CXXFLAGS=-stdlib=libc++ + EOF - name: check compiler version run: | - clang++-21 --version + "$CXX" --version - - name: build and run test with clang 21 + - name: build and run test with clang 21 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=clang-21 -DCMAKE_CXX_COMPILER=clang++-21 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - mapfile -t FILES < <(find include tests benchmarks build/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + mapfile -t FILES < <(find include tests benchmarks build-cmake/examples_from_docs tools -type f \( -name '*.h' -o -name '*.ixx' -o -name '*.cpp' \)) echo "Running clang-format on ${#FILES[@]} files: ${FILES[*]}" clang-format-21 --dry-run --Werror "${FILES[@]}" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh clang++-21 build/drop/env-info.json + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with clang 21 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-clang - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-compatibility.yml b/.github/workflows/bvt-compatibility.yml index 83f41df..6dda774 100644 --- a/.github/workflows/bvt-compatibility.yml +++ b/.github/workflows/bvt-compatibility.yml @@ -4,63 +4,46 @@ on: jobs: bvt-compatibility: runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + compiler: + - {family: gcc, version: 13, modules: false} + - {family: gcc, version: 14, modules: true} + - {family: clang, version: 16, modules: false} + - {family: clang, version: 17, modules: false} + - {family: clang, version: 18, modules: false} + - {family: clang, version: 19, modules: false} + - {family: clang, version: 20, modules: true} + steps: - uses: actions/checkout@v4 - - name: install compilers - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 20 - sudo apt install -y gcc-13 g++-13 gcc-14 g++-14 clang-16 clang-17 clang-18 clang-19 libc++-17-dev - - - name: check compiler versions - run: | - g++-13 --version - g++-14 --version - clang++-16 --version - clang++-17 --version - clang++-18 --version - clang++-19 --version - clang++-20 --version - - - name: build and run test with gcc 14 - run: | - cmake -B build-gcc-14 -GNinja -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build-gcc-14 -j - ctest --test-dir build-gcc-14 -j - - - name: build and run test with gcc 13 - run: | - cmake -B build-gcc-13 -DCMAKE_C_COMPILER=gcc-13 -DCMAKE_CXX_COMPILER=g++-13 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-gcc-13 -j - ctest --test-dir build-gcc-13 -j - - - name: build and run test with clang 20 - run: | - cmake -B build-clang-20 -GNinja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build-clang-20 -j - ctest --test-dir build-clang-20 -j - - name: build and run test with clang 19 + - name: install gcc + if: ${{ matrix.compiler.family == 'gcc' }} run: | - cmake -B build-clang-19 -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-19 -j - ctest --test-dir build-clang-19 -j + sudo apt install -y 'g++-${{ matrix.compiler.version }}' + cat <<'EOF' >> "$GITHUB_ENV" + CC=gcc-${{ matrix.compiler.version }} + CXX=g++-${{ matrix.compiler.version }} + EOF - - name: build and run test with clang 18 + - name: install clang + if: ${{ matrix.compiler.family == 'clang' }} run: | - cmake -B build-clang-18 -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-18 -j - ctest --test-dir build-clang-18 -j + sudo apt install -y 'clang-${{ matrix.compiler.version }}' 'clang-tools-${{ matrix.compiler.version }}' 'libc++-${{ matrix.compiler.version }}-dev' 'libc++abi-${{ matrix.compiler.version }}-dev' + cat <<'EOF' >> "$GITHUB_ENV" + CC=clang-${{ matrix.compiler.version }} + CXX=clang++-${{ matrix.compiler.version }} + CXXFLAGS=-stdlib=libc++ + EOF - - name: build and run test with clang 17 + - name: check compiler version run: | - cmake -B build-clang-17 -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-17 -j - ctest --test-dir build-clang-17 -j + "$CXX" --version - - name: build and run test with clang 16 + - name: build and run test with cmake run: | - cmake -B build-clang-16 -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_CXX_FLAGS="-stdlib=libc++" -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build-clang-16 -j - ctest --test-dir build-clang-16 -j + cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release '-DPROXY_BUILD_MODULES=${{ matrix.compiler.modules }}' + cmake --build build -j + ctest --test-dir build -j diff --git a/.github/workflows/bvt-gcc.yml b/.github/workflows/bvt-gcc.yml index b0909e2..4a5e613 100644 --- a/.github/workflows/bvt-gcc.yml +++ b/.github/workflows/bvt-gcc.yml @@ -8,30 +8,35 @@ jobs: steps: - uses: actions/checkout@v4 - - name: install dependencies + - name: install cmake and meson run: | apt-get update - apt-get install -y cmake ninja-build + apt-get install -y cmake meson ninja-build - name: check compiler version run: | g++ --version - - name: build and run test with gcc 15 + - name: build and run test with gcc 15 on cmake run: | - cmake -B build -GNinja -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=TRUE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh g++ build/drop/env-info.json + ./tools/dump_build_env.sh g++ build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with gcc 15 on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-gcc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-msvc.yml b/.github/workflows/bvt-msvc.yml index bf38ae2..fe06cca 100644 --- a/.github/workflows/bvt-msvc.yml +++ b/.github/workflows/bvt-msvc.yml @@ -7,23 +7,31 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: add cl.exe to PATH uses: ilammy/msvc-dev-cmd@v1 - - name: build and run test with MSVC + - name: build and run test with MSVC on cmake run: | - cmake -B build -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` - && cmake --build build --config Release -j ` - && ctest --test-dir build -j ` - && mkdir build\drop > $null ` - && .\tools\dump_build_env_msvc.ps1 -OutputPath build\drop\env-info.json + cmake -B build-cmake -DCMAKE_CXX_STANDARD=23 -DPROXY_BUILD_MODULES=TRUE ` + && cmake --build build-cmake --config Release -j ` + && ctest --test-dir build-cmake -j ` + && mkdir build-cmake\drop > $null ` + && .\tools\dump_build_env_msvc.ps1 -OutputPath build-cmake\drop\env-info.json - - name: run benchmarks + - name: build and run test with MSVC on meson run: | - build\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build\drop\benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt --vsenv + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake\benchmarks\Release\msft_proxy_benchmarks.exe --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake\drop\benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-msvc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-nvhpc.yml b/.github/workflows/bvt-nvhpc.yml index 9a33331..e9c1eea 100644 --- a/.github/workflows/bvt-nvhpc.yml +++ b/.github/workflows/bvt-nvhpc.yml @@ -10,29 +10,41 @@ jobs: - name: free disk space uses: jlumbroso/free-disk-space@v1.3.1 + # - name: install meson + # # FIXME: install from upstream once https://github.com/mesonbuild/meson/pull/15353 is released + # run: pipx install git+https://github.com/mesonbuild/meson@02b85a846629090a0c7f18e860bab0a10ea4349b + - name: install NVHPC 25.11 run: | curl https://developer.download.nvidia.com/hpc-sdk/ubuntu/DEB-GPG-KEY-NVIDIA-HPC-SDK | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg echo 'deb [signed-by=/usr/share/keyrings/nvidia-hpcsdk-archive-keyring.gpg] https://developer.download.nvidia.com/hpc-sdk/ubuntu/amd64 /' | sudo tee /etc/apt/sources.list.d/nvhpc.list sudo apt-get update -y sudo apt-get install -y nvhpc-25-11 + cat<<'EOF' >> "$GITHUB_ENV" + CC=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc + CXX=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin/nvc++ + EOF - - name: build and run test with NVHPC 25.11 + - name: build and run test with NVHPC 25.11 on cmake run: | - PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/25.11/compilers/bin:$PATH; export PATH - cmake -B build -GNinja -DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++ -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop + cmake -B build-cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPROXY_BUILD_MODULES=FALSE + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh nvc++ build/drop/env-info.json + ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json + + # - name: build and run test with NVHPC 25.11 on meson + # run: | + # meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + # meson test -C build-meson + # meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true - name: run benchmarks - run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-nvhpc - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/bvt-oneapi.yml b/.github/workflows/bvt-oneapi.yml index 3bc1d43..86f1914 100644 --- a/.github/workflows/bvt-oneapi.yml +++ b/.github/workflows/bvt-oneapi.yml @@ -7,9 +7,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: install meson + run: pipx install meson + - name: install libc++ run: | sudo apt-get install -y libc++-19-dev libc++abi-19-dev + cat <<'EOF' >> "$GITHUB_ENV" + CXXFLAGS=-stdlib=libc++ + EOF - name: install intel oneAPI run: | @@ -18,28 +24,36 @@ jobs: sudo apt update sudo apt install -y intel-oneapi-compiler-dpcpp-cpp source /opt/intel/oneapi/setvars.sh - echo "PATH=$PATH" >> $GITHUB_ENV - echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV + cat<<-EOF >> "$GITHUB_ENV" + PATH=$PATH + LD_LIBRARY_PATH=$LD_LIBRARY_PATH + CC=$(which icx) + CXX=$(which icpx) + EOF - name: check compiler version run: | - icpx --version + "$CXX" --version - - name: build and run test with oneapi + - name: build and run test with oneapi on cmake run: | - cmake -B build -GNinja -DCMAKE_CXX_COMPILER=icpx -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-stdlib=libc++" - cmake --build build -j - ctest --test-dir build -j - mkdir build/drop - chmod +x tools/dump_build_env.sh - ./tools/dump_build_env.sh icpx build/drop/env-info.json + cmake -B build-cmake -GNinja -DCMAKE_CXX_STANDARD=23 -DCMAKE_BUILD_TYPE=Release + cmake --build build-cmake -j + ctest --test-dir build-cmake -j + mkdir build-cmake/drop + bash ./tools/dump_build_env.sh "$CXX" build-cmake/drop/env-info.json - - name: run benchmarks + - name: build and run test with oneapi on meson run: | - build/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build/drop/benchmarking-results.json + meson setup build-meson --buildtype=release -Dtests=enabled -Dbenchmarks=enabled --force-fallback-for=fmt + meson test -C build-meson + meson test -C build-meson --benchmark --test-args=--benchmark_list_tests=true + + - name: run benchmarks + run: build-cmake/benchmarks/msft_proxy_benchmarks --benchmark_min_warmup_time=0.1 --benchmark_min_time=0.1s --benchmark_repetitions=30 --benchmark_enable_random_interleaving=true --benchmark_report_aggregates_only=true --benchmark_format=json > build-cmake/drop/benchmarking-results.json - name: archive benchmarking results uses: actions/upload-artifact@v4 with: name: drop-oneapi - path: build/drop/ + path: build-cmake/drop/ diff --git a/.github/workflows/pipeline-release.yml b/.github/workflows/pipeline-release.yml index 6777874..77a9d2c 100644 --- a/.github/workflows/pipeline-release.yml +++ b/.github/workflows/pipeline-release.yml @@ -17,8 +17,8 @@ jobs: version=$(grep -oP 'msft_proxy\d+\s+VERSION\s+\K[0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt) git tag "$version" git push origin "$version" - tar -czf "proxy-$version.tgz" $(git ls-files 'include/**.h' 'include/**.ixx') - echo "PRO_VER=$version" >> $GITHUB_OUTPUT + git ls-files 'include/**.h' 'include/**.ixx' | xargs tar -czf "proxy-$version.tgz" + echo "PRO_VER=$version" >> "$GITHUB_OUTPUT" shell: bash - name: create release draft diff --git a/.gitignore b/.gitignore index 631c244..d2e81b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ # Ignore build directories build/ Testing/ + +# Python bytecode cache +__pycache__/ + +# Meson subprojects +/subprojects/* +!/subprojects/*.wrap diff --git a/benchmarks/meson.build b/benchmarks/meson.build new file mode 100644 index 0000000..f878c36 --- /dev/null +++ b/benchmarks/meson.build @@ -0,0 +1,13 @@ +benchmark( + 'ProxyBenchmarks', + executable( + 'msft_proxy_benchmarks', + files( + 'proxy_creation_benchmark.cpp', + 'proxy_operation_benchmark.cpp', + 'proxy_operation_benchmark_context.cpp', + ), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, benchmark_dep], + ), +) diff --git a/docs/meson.build b/docs/meson.build new file mode 100644 index 0000000..1b18b77 --- /dev/null +++ b/docs/meson.build @@ -0,0 +1,39 @@ +extract_doctest_exe = find_program('../tools/meson_extract_doctest.py') + +docs = run_command( + extract_doctest_exe, + meson.current_source_dir(), + capture: true, + check: true, +).stdout().strip('\0').split('\0') + +examples = [] + +foreach doc : docs + deps = [msft_proxy4_dep] + if doc.contains('fmt') + deps += fmt_dep + endif + + name = doc.replace('/', '_').substring(0, -3) + example_exe = executable( + f'example_@name@', + custom_target( + command: [extract_doctest_exe, '@INPUT@', '@OUTPUT@'], + input: doc, + output: f'@name@.cpp', + ), + extra_files: doc, + implicit_include_directories: false, + dependencies: deps, + build_by_default: false, + ) + examples += example_exe + test( + name, + example_exe, + suite: 'ProxyExamples', + ) +endforeach + +alias_target('examples', examples) diff --git a/docs/spec/proxy_view.md b/docs/spec/proxy_view.md index 3fb1c5a..2f81c5d 100644 --- a/docs/spec/proxy_view.md +++ b/docs/spec/proxy_view.md @@ -37,6 +37,7 @@ using proxy_view = proxy>; ## Example ```cpp +#include #include #include diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index 923e41c..dcebcd3 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -23,6 +23,10 @@ #if __has_include() #include #endif // __has_include() +#if __cpp_lib_format >= 201907L || \ + (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 170000) +#define PRO4D_HAS_FORMAT +#endif // __cpp_lib_format || _LIBCPP_VERSION >= 170000 #endif // __STDC_HOSTED__ #if __cpp_rtti >= 199711L @@ -2125,7 +2129,7 @@ struct weak_conversion_dispatch : cast_dispatch_base { template using weak_conversion_overload = weak_proxy() const noexcept; -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template struct format_overload_traits; template <> @@ -2166,7 +2170,7 @@ struct format_dispatch { return impl.format(self, fc); } }; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L struct proxy_cast_context { @@ -2273,7 +2277,7 @@ struct proxy_typeid_reflector { namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT template using format = typename FB::template add_convention using wformat = typename FB::template add_convention>; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L template @@ -2604,7 +2608,7 @@ struct weak_dispatch : D { // == Adapters (std::formatter) == // ============================================================================= -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT namespace std { template @@ -2635,7 +2639,7 @@ struct formatter, CharT> { }; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #undef PROD_UNREACHABLE #undef PROD_NO_UNIQUE_ADDRESS_ATTRIBUTE diff --git a/include/proxy/v4/proxy.ixx b/include/proxy/v4/proxy.ixx index f1ad598..37fb981 100644 --- a/include/proxy/v4/proxy.ixx +++ b/include/proxy/v4/proxy.ixx @@ -41,10 +41,10 @@ using v4::weak_proxy; namespace skills { -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT using skills::format; using skills::wformat; -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT #if __cpp_rtti >= 199711L using skills::direct_rtti; @@ -60,10 +60,10 @@ using skills::slim; } // namespace pro::inline v4 -#if __STDC_HOSTED__ && __has_include() +#ifdef PRO4D_HAS_FORMAT export namespace std { using std::formatter; } // namespace std -#endif // __STDC_HOSTED__ && __has_include() +#endif // PRO4D_HAS_FORMAT diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..24d5a84 --- /dev/null +++ b/meson.build @@ -0,0 +1,118 @@ +project( + 'msft_proxy4', + 'cpp', + version: '4.0.1', + license: 'MIT', + license_files: 'LICENSE', + meson_version: '>=1.3', + default_options: { + 'cpp_std': ['vc++latest', 'c++26', 'c++23', 'c++20'], + 'warning_level': '3', + }, +) + +cxx = meson.get_compiler('cpp') +if cxx.compute_int('__has_cpp_attribute(msvc::no_unique_address)') != 0 and not cxx.compiles( + ''' + struct A {}; + + struct B { + [[msvc::no_unique_address]] A val; + }; + + struct C : B { + char x; + }; + + static_assert(sizeof(C) == 1); + ''', + name: 'msvc::no_unique_address behaves correctly', +) + error( + 'unsupported compiler:', + 'https://github.com/llvm/llvm-project/issues/143245', + ) +endif +if cxx.get_argument_syntax() == 'msvc' + add_project_arguments( + '/bigobj', + language: 'cpp', + ) +endif + +inc = include_directories('include') + +hdrs = files( + 'include/proxy/proxy.h', + 'include/proxy/proxy_fmt.h', + 'include/proxy/proxy_macros.h', +) +hdrs_v4 = files( + 'include/proxy/v4/proxy.h', + 'include/proxy/v4/proxy_fmt.h', + 'include/proxy/v4/proxy_macros.h', +) +hdrs_mod = files('include/proxy/v4/proxy.ixx') + +msft_proxy4_dep = declare_dependency( + sources: hdrs + hdrs_v4, + include_directories: inc, +) + +install_headers( + hdrs, + subdir: 'proxy', +) +install_headers( + hdrs_v4, + subdir: 'proxy/v4', +) +install_headers( + hdrs_mod, + subdir: 'proxy/v4', +) +pkgconfig = import( + 'pkgconfig', + required: false, +) +if pkgconfig.found() + pkgconfig.generate( + name: meson.project_name(), + description: 'Next Generation Polymorphism in C++', + url: 'https://microsoft.github.io/proxy/', + ) +endif + +meson.override_dependency(meson.project_name(), msft_proxy4_dep) + +tests = get_option('tests').disable_auto_if(meson.is_subproject()) +gtest_dep = dependency( + 'gtest_main', + main: true, + required: tests, +) +fmt_dep = dependency( + 'fmt', + version: '>=6.1.0', + required: false, + disabler: true, +) +subdir( + 'tests', + if_found: gtest_dep, +) + +benchmarks = get_option('benchmarks').disable_auto_if(meson.is_subproject()) +benchmark_dep = dependency( + 'benchmark_main', + required: benchmarks, +) +subdir( + 'benchmarks', + if_found: benchmark_dep, +) + +examples = get_option('examples').disable_auto_if(meson.is_subproject()) +if examples.allowed() + subdir('docs') +endif diff --git a/meson.format b/meson.format new file mode 100644 index 0000000..66e2f72 --- /dev/null +++ b/meson.format @@ -0,0 +1,4 @@ +indent_by=' ' +end_of_line=lf +insert_final_newline=true +kwargs_force_multiline=true diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..584693a --- /dev/null +++ b/meson.options @@ -0,0 +1,18 @@ +option( + 'tests', + type: 'feature', + value: 'auto', + description: 'Build tests', +) +option( + 'benchmarks', + type: 'feature', + value: 'auto', + description: 'Build benchmarks', +) +option( + 'examples', + type: 'feature', + value: 'auto', + description: 'Extract and build examples from docs', +) diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap new file mode 100644 index 0000000..6544857 --- /dev/null +++ b/subprojects/fmt.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = fmt-12.1.0 +source_url = https://github.com/fmtlib/fmt/archive/12.1.0.tar.gz +source_filename = fmt-12.1.0.tar.gz +source_hash = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea +source_fallback_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt-12.1.0.tar.gz +patch_filename = fmt_12.1.0-4_patch.zip +patch_url = https://github.com/wrapdb/fmt/releases/download/12.1.0-4/fmt_12.1.0-4_patch.zip +patch_hash = 65b7fe3c29f25528011bc295e83e4f6f10028c922407e003b7856bb79789f345 +3rdparty_wrapdb_version = 12.1.0-4 + +[provide] +dependency_names = fmt diff --git a/subprojects/google-benchmark.wrap b/subprojects/google-benchmark.wrap new file mode 100644 index 0000000..7e2e6e7 --- /dev/null +++ b/subprojects/google-benchmark.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = benchmark-1.8.4 +source_url = https://github.com/google/benchmark/archive/refs/tags/v1.8.4.tar.gz +source_filename = benchmark-1.8.4.tar.gz +source_hash = 3e7059b6b11fb1bbe28e33e02519398ca94c1818874ebed18e504dc6f709be45 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/google-benchmark_1.8.4-5/benchmark-1.8.4.tar.gz +patch_filename = google-benchmark_1.8.4-5_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/google-benchmark_1.8.4-5/get_patch +patch_hash = 671ffed65f1e95e8c20edb7a06eb54476797e58169160b255f52dc71f4b83957 +wrapdb_version = 1.8.4-5 + +[provide] +dependency_names = benchmark, benchmark_main diff --git a/subprojects/gtest.wrap b/subprojects/gtest.wrap new file mode 100644 index 0000000..9902a4f --- /dev/null +++ b/subprojects/gtest.wrap @@ -0,0 +1,16 @@ +[wrap-file] +directory = googletest-1.17.0 +source_url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz +source_filename = googletest-1.17.0.tar.gz +source_hash = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c +patch_filename = gtest_1.17.0-4_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-4/get_patch +patch_hash = 3abf7662d09db706453a5b064a1e914678c74b9d9b0b19382747ca561d0d8750 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-4/googletest-1.17.0.tar.gz +wrapdb_version = 1.17.0-4 + +[provide] +gtest = gtest_dep +gtest_main = gtest_main_dep +gmock = gmock_dep +gmock_main = gmock_main_dep diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..fbf36f2 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,67 @@ +test_srcs = files( + 'proxy_creation_tests.cpp', + 'proxy_dispatch_tests.cpp', + 'proxy_format_tests.cpp', + 'proxy_integration_tests.cpp', + 'proxy_invocation_tests.cpp', + 'proxy_lifetime_tests.cpp', + 'proxy_reflection_tests.cpp', + 'proxy_regression_tests.cpp', + 'proxy_rtti_tests.cpp', + 'proxy_traits_tests.cpp', + 'proxy_view_tests.cpp', +) + +test_deps = [msft_proxy4_dep, gtest_dep] + +if fmt_dep.found() + test_srcs += files( + 'proxy_fmt_format_tests.cpp', + ) + test_deps += fmt_dep +endif + +test( + 'ProxyTests', + executable( + 'msft_proxy_tests', + test_srcs, + implicit_include_directories: false, + dependencies: test_deps, + build_by_default: false, + ), + protocol: 'gtest', +) + +freestanding_cflags = ['-ffreestanding'] +freestanding_ldflags = ['-nodefaultlibs'] + +if cxx.has_multi_arguments( + freestanding_cflags, + freestanding_ldflags, + required: false, +) + libc_dep = cxx.find_library( + 'c', + required: false, + ) + + if libc_dep.found() + test( + 'ProxyFreestandingSupportTests', + executable( + 'msft_proxy_freestanding_tests', + files('freestanding/proxy_freestanding_tests.cpp'), + implicit_include_directories: false, + dependencies: [msft_proxy4_dep, libc_dep], + cpp_args: freestanding_cflags + freestanding_ldflags, + link_args: get_option('b_sanitize') == 'none' ? freestanding_ldflags : [], + override_options: { + 'cpp_eh': 'none', + 'cpp_rtti': false, + }, + build_by_default: false, + ), + ) + endif +endif diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index e2a71aa..ca7fc85 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -829,6 +829,7 @@ TEST(ProxyDispatchTests, TestFreeAsMemDispatch) { } TEST(ProxyDispatchTests, TestSubstitutionDispatch) { +#ifdef PRO4D_HAS_FORMAT struct Base : pro::facade_builder // ::add_skill // ::build {}; @@ -844,4 +845,7 @@ TEST(ProxyDispatchTests, TestSubstitutionDispatch) { pro::proxy p3 = std::move(p1); ASSERT_FALSE(p1.has_value()); ASSERT_EQ(std::format("{}", *p3), "123"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp index 7b0d224..8947973 100644 --- a/tests/proxy_format_tests.cpp +++ b/tests/proxy_format_tests.cpp @@ -4,6 +4,7 @@ #include #include +#ifdef PRO4D_HAS_FORMAT namespace proxy_format_tests_details { struct NonFormattable : pro::facade_builder::build {}; @@ -29,17 +30,26 @@ static_assert( } // namespace proxy_format_tests_details namespace details = proxy_format_tests_details; +#endif // PRO4D_HAS_FORMAT TEST(ProxyFormatTests, TestFormat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format("{}", *p), "123"); ASSERT_EQ(std::format("{:*<6}", *p), "123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } TEST(ProxyFormatTests, TestWformat) { +#ifdef PRO4D_HAS_FORMAT int v = 123; pro::proxy p = &v; ASSERT_EQ(std::format(L"{}", *p), L"123"); ASSERT_EQ(std::format(L"{:*<6}", *p), L"123***"); +#else + GTEST_SKIP() << "std::format not available"; +#endif // PRO4D_HAS_FORMAT } diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 2041e9a..5cc90b4 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -17,7 +17,8 @@ struct TraitsReflector { is_copy_constructible_(std::is_copy_constructible_v), is_nothrow_move_constructible_(std::is_nothrow_move_constructible_v), is_nothrow_destructible_(std::is_nothrow_destructible_v), - is_trivial_(std::is_trivial_v) {} + is_trivial_(std::is_trivially_default_constructible_v && + std::is_trivially_copyable_v) {} template struct accessor { diff --git a/tools/extract_example_code_from_docs.py b/tools/extract_example_code_from_docs.py index 405c1dc..becbb94 100644 --- a/tools/extract_example_code_from_docs.py +++ b/tools/extract_example_code_from_docs.py @@ -1,42 +1,79 @@ -import os +#!/usr/bin/env python3 +# pyright: strict + import re -import sys +from typing import Optional +from pathlib import Path + +EXAMPLE_PATTERN = re.compile(r"## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```", re.DOTALL) + -def extract_cpp_code(md_path, cpp_path): - with open(md_path, 'r', encoding='utf-8') as f: +def extract_cpp_code(md_path: Path) -> Optional[str]: + with open(md_path, "r", encoding="utf-8") as f: content = f.read() - pattern = r'## Example\r?\n\r?\n```cpp\r?\n(.*?)\r?\n```' - code_blocks = re.findall(pattern, content, re.DOTALL) + code_blocks: list[str] = re.findall(EXAMPLE_PATTERN, content) if len(code_blocks) == 0: return # No match, skip elif len(code_blocks) > 1: - raise ValueError(f"File '{md_path}' contains more than one '## Example' C++ code block.") + msg = f"File '{md_path}' contains more than one '## Example' C++ code block." + raise ValueError(msg) cpp_code = code_blocks[0] - header = f"// This file was auto-generated from:\n// {md_path}\n\n" + header = f""" +// This file was auto-generated from: +// {md_path} + +""".lstrip() + + if "pro::skills::format" in cpp_code: + cpp_code = f""" +#include +#ifdef PRO4D_HAS_FORMAT +{cpp_code} +#else +int main() {{ + // std::format not available + return 77; +}} +#endif +""".strip() - with open(cpp_path, 'w', encoding='utf-8') as out: - out.write(header) - out.write(cpp_code) + return header + cpp_code -def main(): - if len(sys.argv) != 3: - print("Usage: python extract_example_code_from_docs.py ") - sys.exit(1) - input_dir = sys.argv[1] - output_dir = sys.argv[2] +def main() -> None: + import argparse + import os + from dataclasses import dataclass + + @dataclass(frozen=True) + class Args: + input_dir: Path + output_dir: Path + + parser = argparse.ArgumentParser() + _ = parser.add_argument("input_dir", type=Path, help="Path to Markdown documents") + _ = parser.add_argument("output_dir", type=Path, help="Source code output path") + args = parser.parse_args(namespace=Args) + + input_dir = args.input_dir + output_dir = args.output_dir for root, _, files in os.walk(input_dir): for file in files: - if file.endswith('.md'): - md_path = os.path.join(root, file) - rel_path = os.path.relpath(md_path, input_dir) - rel_base = os.path.splitext(rel_path)[0].replace(os.sep, '_') - cpp_path = os.path.join(output_dir, f"example_{rel_base}.cpp") - extract_cpp_code(md_path, cpp_path) - -if __name__ == '__main__': + if file.endswith(".md"): + md_path = Path(root) / file + rel_path = md_path.relative_to(input_dir) + rel_base = "_".join([*rel_path.parent.parts, rel_path.stem]) + cpp_path = output_dir / f"example_{rel_base}.cpp" + cpp_code = extract_cpp_code(md_path) + if cpp_code is None: + continue + with open(cpp_path, "w", encoding="utf-8") as f: + _ = f.write(cpp_code) + + +if __name__ == "__main__": main() diff --git a/tools/meson_extract_doctest.py b/tools/meson_extract_doctest.py new file mode 100644 index 0000000..63bfc46 --- /dev/null +++ b/tools/meson_extract_doctest.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# pyright: strict + +import sys +from pathlib import Path + +from extract_example_code_from_docs import extract_cpp_code + +infile = Path(sys.argv[1]) +if infile.is_dir(): + for path in infile.glob("**/*.md"): + if extract_cpp_code(path) is None: + continue + print(str(path.relative_to(infile).as_posix()), end="\0") + exit() + +outfile = Path(sys.argv[2]) + +cpp_code = extract_cpp_code(infile) + +if not cpp_code: + raise ValueError + +with open(outfile, "w") as f: + _ = f.write(cpp_code) From e78f479ece71dabf0683eccbe3b2218d93240e57 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 29 Jan 2026 16:10:51 +0800 Subject: [PATCH 10/14] Update branding for migration (#1) --- .github/workflows/bvt-report.yml | 4 +- CODE_OF_CONDUCT.md | 10 +- LICENSE | 2 +- README.md | 106 ++++++++---------- SECURITY.md | 40 ++----- SUPPORT.md | 27 ++--- benchmarks/proxy_creation_benchmark.cpp | 2 +- benchmarks/proxy_operation_benchmark.cpp | 2 +- .../proxy_operation_benchmark_context.cpp | 2 +- .../proxy_operation_benchmark_context.h | 2 +- docs/faq.md | 6 +- docs/resources/icon.png | Bin 2323 -> 28503 bytes include/proxy/proxy.h | 2 +- include/proxy/proxy_fmt.h | 2 +- include/proxy/proxy_macros.h | 2 +- include/proxy/v4/proxy.h | 2 +- include/proxy/v4/proxy_fmt.h | 2 +- include/proxy/v4/proxy_macros.h | 2 +- meson.build | 2 +- mkdocs.yml | 6 +- mkdocs/overrides/main.html | 2 +- tests/proxy_creation_tests.cpp | 2 +- tests/proxy_dispatch_tests.cpp | 2 +- tests/proxy_fmt_format_tests.cpp | 2 +- tests/proxy_format_tests.cpp | 2 +- tests/proxy_integration_tests.cpp | 2 +- tests/proxy_invocation_tests.cpp | 2 +- tests/proxy_lifetime_tests.cpp | 2 +- tests/proxy_reflection_tests.cpp | 2 +- tests/proxy_regression_tests.cpp | 2 +- tests/proxy_rtti_tests.cpp | 2 +- tests/proxy_traits_tests.cpp | 2 +- tests/proxy_view_tests.cpp | 2 +- tests/utils.h | 2 +- tools/report_generator/main.cpp | 2 +- 35 files changed, 108 insertions(+), 145 deletions(-) diff --git a/.github/workflows/bvt-report.yml b/.github/workflows/bvt-report.yml index 2b3a104..087a6b9 100644 --- a/.github/workflows/bvt-report.yml +++ b/.github/workflows/bvt-report.yml @@ -23,8 +23,8 @@ jobs: cat < benchmarking-report.md # Benchmarking Report - - Generated for: [Microsoft "Proxy" library](https://github.com/microsoft/proxy) - - Commit ID: [${{ github.sha }}](https://github.com/microsoft/proxy/commit/${{ github.sha }}) + - Generated for: [C++ Proxy library](https://github.com/ngcpp/proxy) + - Commit ID: [${{ github.sha }}](https://github.com/ngcpp/proxy/commit/${{ github.sha }}) - Generated at: $(date -u +"%Y-%m-%dT%H:%M:%SZ") EOF diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f9ba8cf..97f59e1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,9 +1,9 @@ -# Microsoft Open Source Code of Conduct +# Next Gen C++ Foundation Code of Conduct -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). Resources: -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) +- [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) +- Contact [conduct@ngcpp.org](mailto:conduct@ngcpp.org) with questions or concerns diff --git a/LICENSE b/LICENSE index 9e841e7..84c4f3f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License - Copyright (c) Microsoft Corporation. + Copyright (c) 2022-2026 Microsoft Corporation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9d55153..14be88c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Proxy: Next Generation Polymorphism in C++ -[![Proxy-CI](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml/badge.svg)](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml) +[![Proxy-CI](https://github.com/ngcpp/proxy/actions/workflows/pipeline-ci.yml/badge.svg)](https://github.com/ngcpp/proxy/actions/workflows/pipeline-ci.yml) Are you looking to simplify the lifetime management and maintenance of polymorphic objects in C++? @@ -16,7 +16,7 @@ If so, this library is for you. "Proxy" is a modern C++ library that helps you use polymorphism (a way to use different types of objects interchangeably) without needing inheritance. -"Proxy" was created by Microsoft engineers and has been used in the Windows operating system since 2022. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: +"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, and has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). This repository was ported from https://github.com/microsoft/proxy, where more historical releases can be found. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: - **Portable**: "Proxy" was implemented as a header-only library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded engineering or kernel design of an operating system. - **Non-intrusive**: An implementation type is no longer required to inherit from an abstract binding. @@ -25,11 +25,11 @@ If so, this library is for you. - **Accessible**: Learned from user feedback, accessibility has been significantly improved with intuitive syntax, good IDE compatibility, and accurate diagnostics. - **Flexible**: Not only member functions, the "abstraction" of "Proxy" allows *any* expression to be polymorphic, including free functions, operators, conversions, etc. Different abstractions can be freely composed on demand. Performance tuning is supported for experts to balance between extensibility and performance. -Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.github.io/proxy/faq) for more background, and refer to the [specifications](https://microsoft.github.io/proxy/spec) for more technical details. +Please refer to the [Proxy's Frequently Asked Questions](https://ngcpp.github.io/proxy/faq) for more background, and refer to the [specifications](https://ngcpp.github.io/proxy/spec) for more technical details. ## Quick Start -"Proxy" is a header-only C++20 library. To use the library, make sure your compiler meets the [minimum requirements](#compiler-req) and just put the [proxy](https://github.com/microsoft/proxy/tree/main/include/proxy) directory in your project's include directory. Alternatively, you can install the library via: +"Proxy" is a header-only C++20 library. To use the library, make sure your compiler meets the [minimum requirements](#compiler-req) and just put the [proxy](https://github.com/ngcpp/proxy/tree/main/include/proxy) directory in your project's include directory. Alternatively, you can install the library via: - [vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/overview): [proxy port on vcpkg.io](https://vcpkg.io/en/package/proxy) - [conan](https://conan.io/): [proxy recipe on conan.io](https://conan.io/center/recipes/proxy) @@ -39,7 +39,7 @@ Please refer to the [Proxy's Frequently Asked Questions](https://microsoft.githu CPMAddPackage( NAME msft_proxy4 GIT_TAG 4.0.0 # or above - GIT_REPOSITORY https://github.com/microsoft/proxy.git + GIT_REPOSITORY https://github.com/ngcpp/proxy.git ) target_link_libraries(main PRIVATE msft_proxy4::proxy) @@ -79,22 +79,22 @@ Here is a step-by-step explanation: - `#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 `: For the "Proxy" library. Most of the facilities of the library are defined in namespace `pro`. -- `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/spec/ProFacade), is how the "Proxy" library models runtime abstraction. Specifically, - - [`pro::facade_builder`](https://microsoft.github.io/proxy/spec/basic_facade_builder): Provides capability to build a facade type at compile-time. - - [`add_skill`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_skill)`<`[`pro::skills::format`](https://microsoft.github.io/proxy/spec/skills_format)`>`: 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/spec/proxy)` 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. +- `struct Formattable : pro::facade_builder ... ::build {}`: Defines a facade type `Formattable`. The term "facade", formally defined as the [*ProFacade* requirements](https://ngcpp.github.io/proxy/spec/ProFacade), is how the "Proxy" library models runtime abstraction. Specifically, + - [`pro::facade_builder`](https://ngcpp.github.io/proxy/spec/basic_facade_builder): Provides capability to build a facade type at compile-time. + - [`add_skill`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_skill)`<`[`pro::skills::format`](https://ngcpp.github.io/proxy/spec/skills_format)`>`: Specifies the capability of formatting (via [standard formatting functions](https://en.cppreference.com/w/cpp/utility/format)). + - [`build`](https://ngcpp.github.io/proxy/docs/basic_facade_builder/build.html): Builds the context into a facade type. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` 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/spec/proxy)` 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. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` 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/spec/proxy)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/spec/make_proxy)`(3.14159)`: Creates a `proxy` from a `double` without specifying the underlying pointer type. Specifically, +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://ngcpp.github.io/proxy/spec/make_proxy)`(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/spec/make_proxy) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://microsoft.github.io/proxy/spec/make_proxy_inplace), which guarantees no 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://ngcpp.github.io/proxy/spec/make_proxy) realizes the fact at compile-time, and falls back to [`pro::make_proxy_inplace`](https://ngcpp.github.io/proxy/spec/make_proxy_inplace), 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`. -Note: If you prefer the library to be consumed as a (C++20) module, refer to [C++20 Modules support](https://microsoft.github.io/proxy/modules_support). +Note: If you prefer the library to be consumed as a (C++20) module, refer to [C++20 Modules support](https://ngcpp.github.io/proxy/modules_support). ### Hello World (Stream Version) @@ -131,28 +131,28 @@ Here is a step-by-step explanation: - `#include `: For [`std::string`](https://en.cppreference.com/w/cpp/string/basic_string). - `#include `: For the "Proxy" library. - `struct Streamable : pro::facade_builder ... ::build {}`: Defines a facade type `Streamable`. Specifically, - - [`pro::facade_builder`](https://microsoft.github.io/proxy/spec/basic_facade_builder): Gets prepared to build another facade. - - [`add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention): Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context. - - [`pro::operator_dispatch`](https://microsoft.github.io/proxy/spec/operator_dispatch)`<"<<", 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. + - [`pro::facade_builder`](https://ngcpp.github.io/proxy/spec/basic_facade_builder): Gets prepared to build another facade. + - [`add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention): Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context. + - [`pro::operator_dispatch`](https://ngcpp.github.io/proxy/spec/operator_dispatch)`<"<<", 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/spec/basic_facade_builder/build): Builds the context into a facade type. + - [`build`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/build): Builds the context into a facade type. -- [`pro::proxy`](https://microsoft.github.io/proxy/spec/proxy)` p1 = &str`: Creates a `proxy` object from a raw pointer of `std::string`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` 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/spec/proxy)` 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`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` 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/spec/proxy)` p3 = `[`pro::make_proxy`](https://microsoft.github.io/proxy/spec/make_proxy)`(3.14)`: Creates a `proxy` from a `double`. +- [`pro::proxy`](https://ngcpp.github.io/proxy/spec/proxy)` p3 = `[`pro::make_proxy`](https://ngcpp.github.io/proxy/spec/make_proxy)`(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 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/spec/PRO_DEF_MEM_DISPATCH): Defines a dispatch type for member function call expressions, providing accessibility as member functions. -- [macro `PRO_DEF_FREE_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_FREE_DISPATCH): 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/spec/PRO_DEF_FREE_AS_MEM_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as member functions. -- [class template `pro::operator_dispatch`](https://microsoft.github.io/proxy/spec/operator_dispatch): Dispatch type for operator expressions. -- [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](https://microsoft.github.io/proxy/spec/explicit_conversion_dispatch) and [class `implicit_conversion_dispatch`](https://microsoft.github.io/proxy/spec/implicit_conversion_dispatch): Dispatch type for conversion expressions. +- [macro `PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH): Defines a dispatch type for member function call expressions, providing accessibility as member functions. +- [macro `PRO_DEF_FREE_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_FREE_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as free functions. +- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_FREE_AS_MEM_DISPATCH): Defines a dispatch type for free function call expressions, providing accessibility as member functions. +- [class template `pro::operator_dispatch`](https://ngcpp.github.io/proxy/spec/operator_dispatch): Dispatch type for operator expressions. +- [class `explicit_conversion_dispatch` (aka. `conversion_dispatch`)](https://ngcpp.github.io/proxy/spec/explicit_conversion_dispatch) and [class `implicit_conversion_dispatch`](https://ngcpp.github.io/proxy/spec/implicit_conversion_dispatch): 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 ([run](https://godbolt.org/z/E95nY7PYq)): @@ -206,11 +206,11 @@ Here is a step-by-step explanation: - `#include `: For [`std::cout`](https://en.cppreference.com/w/cpp/io/cout). - `#include `: For [`std::stringstream`](https://en.cppreference.com/w/cpp/io/basic_stringstream). - `#include `: For the "Proxy" library. -- [`PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemDraw, Draw)`: Defines a dispatch type `MemDraw` for expressions of calling member function `Draw`. -- [`PRO_DEF_MEM_DISPATCH`](https://microsoft.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemArea, Area)`: Defines a dispatch type `MemArea` for expressions of calling member function `Area`. +- [`PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemDraw, Draw)`: Defines a dispatch type `MemDraw` for expressions of calling member function `Draw`. +- [`PRO_DEF_MEM_DISPATCH`](https://ngcpp.github.io/proxy/spec/PRO_DEF_MEM_DISPATCH)`(MemArea, Area)`: Defines a dispatch type `MemArea` for expressions of calling member function `Area`. - `struct Drawable : pro::facade_builder ... ::build {}`: Defines a facade type `Drawable`. Specifically, - - [`add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention): Adds calling conventions to the build context. - - [`support_copy`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_copy)`<`[`pro::constraint_level`](https://microsoft.github.io/proxy/spec/constraint_level)`::nontrivial>`: Specifies the underlying pointer type shall be copyable, which also makes the resulting `proxy` type copyable. + - [`add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention): Adds calling conventions to the build context. + - [`support_copy`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_copy)`<`[`pro::constraint_level`](https://ngcpp.github.io/proxy/spec/constraint_level)`::nontrivial>`: Specifies the underlying pointer type shall be copyable, which also makes the resulting `proxy` type copyable. - `class Rectangle`: An implementation of `Drawable`. - Function `PrintDrawableToString`: Converts a `Drawable` into a `std::string`. Note that this is a function rather than a function template, which means it can generate [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) in a larger build system. - `pro::proxy p = pro::make_proxy(3, 5)`: Creates a `proxy` object containing a `Rectangle`. @@ -219,18 +219,18 @@ Here is a step-by-step explanation: ### Other Useful Features -The "Proxy" library is a self-contained solution for runtime polymorphism in C++. There are many other capabilities documented in the [specifications](https://microsoft.github.io/proxy/spec). In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback: +The "Proxy" library is a self-contained solution for runtime polymorphism in C++. There are many other capabilities documented in the [specifications](https://ngcpp.github.io/proxy/spec). In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback: -- **Overloading**: [`facade_builder::add_convention`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_convention) 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/spec/ProOverload)) and perform standard overload resolution when invoking a `proxy`. -- **Facade composition**: [`facade_builder::add_facade`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_facade) 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/spec/proxiable), [`proxiable_target`](https://microsoft.github.io/proxy/spec/proxiable_target) and [`inplace_proxiable_target`](https://microsoft.github.io/proxy/spec/inplace_proxiable_target). -- **Allocator awareness**: [function template `allocate_proxy`](https://microsoft.github.io/proxy/spec/allocate_proxy) 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/spec/basic_facade_builder) provides full support for constraints configuration, including memory layout (by [`restrict_layout`](https://microsoft.github.io/proxy/spec/basic_facade_builder/restrict_layout)), copyability (by [`support_copy`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_copy)), relocatability (by [`support_relocation`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_relocation)), and destructibility (by [`support_destruction`](https://microsoft.github.io/proxy/spec/basic_facade_builder/support_destruction)). -- **Reflection**: `proxy` supports type-based compile-time reflection for runtime queries. Please refer to [`facade_builder::add_reflection`](https://microsoft.github.io/proxy/spec/basic_facade_builder/add_reflection) and [function template `proxy_reflect`](https://microsoft.github.io/proxy/spec/proxy_reflect) 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/spec/make_proxy_view), [alias template `proxy_view`, class template `observer_facade`](https://microsoft.github.io/proxy/spec/proxy_view) and [`skills::as_view`](https://microsoft.github.io/proxy/spec/skills_as_view) 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 4.0.0, "RTTI for `proxy`" has been implemented as an extension and allows users to opt-in for each facade definition. Please refer to [`skills::rtti`](https://microsoft.github.io/proxy/spec/skills_rtti) 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/spec/make_proxy_shared), [`allocate_proxy_shared`](https://microsoft.github.io/proxy/spec/allocate_proxy_shared), [alias template `weak_proxy`, class template `weak_facade`](https://microsoft.github.io/proxy/spec/weak_proxy) and [`skills::as_weak`](https://microsoft.github.io/proxy/spec/skills_as_weak) 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/spec/weak_dispatch) that throws when invoked. +- **Overloading**: [`facade_builder::add_convention`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_convention) is more powerful than demonstrated above. It can take any number of overload types (formally, any type meeting the [*ProOverload* requirements](https://ngcpp.github.io/proxy/spec/ProOverload)) and perform standard overload resolution when invoking a `proxy`. +- **Facade composition**: [`facade_builder::add_facade`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_facade) allows flexible composition of different abstractions. +- **Concepts**: To facilitate template programming with "Proxy", 3 concepts are exported from a facade type. Namely, [`proxiable`](https://ngcpp.github.io/proxy/spec/proxiable), [`proxiable_target`](https://ngcpp.github.io/proxy/spec/proxiable_target) and [`inplace_proxiable_target`](https://ngcpp.github.io/proxy/spec/inplace_proxiable_target). +- **Allocator awareness**: [function template `allocate_proxy`](https://ngcpp.github.io/proxy/spec/allocate_proxy) 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://ngcpp.github.io/proxy/spec/basic_facade_builder) provides full support for constraints configuration, including memory layout (by [`restrict_layout`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/restrict_layout)), copyability (by [`support_copy`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_copy)), relocatability (by [`support_relocation`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_relocation)), and destructibility (by [`support_destruction`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/support_destruction)). +- **Reflection**: `proxy` supports type-based compile-time reflection for runtime queries. Please refer to [`facade_builder::add_reflection`](https://ngcpp.github.io/proxy/spec/basic_facade_builder/add_reflection) and [function template `proxy_reflect`](https://ngcpp.github.io/proxy/spec/proxy_reflect) 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://ngcpp.github.io/proxy/spec/make_proxy_view), [alias template `proxy_view`, class template `observer_facade`](https://ngcpp.github.io/proxy/spec/proxy_view) and [`skills::as_view`](https://ngcpp.github.io/proxy/spec/skills_as_view) 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 4.0.0, "RTTI for `proxy`" has been implemented as an extension and allows users to opt-in for each facade definition. Please refer to [`skills::rtti`](https://ngcpp.github.io/proxy/spec/skills_rtti) 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://ngcpp.github.io/proxy/spec/make_proxy_shared), [`allocate_proxy_shared`](https://ngcpp.github.io/proxy/spec/allocate_proxy_shared), [alias template `weak_proxy`, class template `weak_facade`](https://ngcpp.github.io/proxy/spec/weak_proxy) and [`skills::as_weak`](https://ngcpp.github.io/proxy/spec/skills_as_weak) 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://ngcpp.github.io/proxy/spec/weak_dispatch) that throws when invoked. ## Minimum Requirements for Compilers @@ -245,7 +245,7 @@ The "Proxy" library is a self-contained solution for runtime polymorphism in C++ ## Build and Run Tests with CMake ``` -git clone https://github.com/microsoft/proxy.git +git clone https://github.com/ngcpp/proxy.git cd proxy cmake -B build cmake --build build -j @@ -264,22 +264,10 @@ ctest --test-dir build -j ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a +This project welcomes contributions and suggestions. Some contributions may require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +the rights to use your contribution. If a CLA is required, the PR bot will provide instructions. -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). +For more information see the [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) or +contact [conduct@ngcpp.org](mailto:conduct@ngcpp.org) with any additional questions or comments. diff --git a/SECURITY.md b/SECURITY.md index 869fdfe..bc82d67 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,41 +1,25 @@ - - ## Security -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. +The Next Gen C++ Foundation (ngcpp) takes security seriously. If you believe you have found a security vulnerability in this repository, please report it responsibly. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. +Instead, please create a private security advisory at: -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. +- https://github.com/ngcpp/proxy/security/advisories/new -## Preferred Languages +If you need to share additional details that do not fit in the advisory, you may email: -We prefer all communications to be in English. +- ngcpp@outlook.com -## Policy +Please include as much of the following as possible to help us triage quickly: -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). +- Type of issue (e.g., buffer overflow, RCE, etc.) +- Affected file paths and versions/commits +- Reproduction steps and any required configuration +- Proof-of-concept (if available) +- Impact assessment - +We aim to acknowledge reports within 72 hours. diff --git a/SUPPORT.md b/SUPPORT.md index 291d4d4..50dfa90 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,25 +1,16 @@ -# TODO: The maintainer of this repo has not yet edited this file - -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. -- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. - -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* - # Support ## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new ones to avoid duplicates. For new issues, file your bug or feature request +as a new issue. + +For help and questions about using this project, please use GitHub Discussions: -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. +- https://github.com/ngcpp/proxy/discussions -## Microsoft Support Policy +## Support Policy -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. +Support for this project is provided on a best-effort basis by the community and maintainers via +the resources listed above. diff --git a/benchmarks/proxy_creation_benchmark.cpp b/benchmarks/proxy_creation_benchmark.cpp index a17a4e4..11d8b29 100644 --- a/benchmarks/proxy_creation_benchmark.cpp +++ b/benchmarks/proxy_creation_benchmark.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/benchmarks/proxy_operation_benchmark.cpp b/benchmarks/proxy_operation_benchmark.cpp index 978d58f..dbb3053 100644 --- a/benchmarks/proxy_operation_benchmark.cpp +++ b/benchmarks/proxy_operation_benchmark.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/benchmarks/proxy_operation_benchmark_context.cpp b/benchmarks/proxy_operation_benchmark_context.cpp index d0523a1..fedcef6 100644 --- a/benchmarks/proxy_operation_benchmark_context.cpp +++ b/benchmarks/proxy_operation_benchmark_context.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "proxy_operation_benchmark_context.h" diff --git a/benchmarks/proxy_operation_benchmark_context.h b/benchmarks/proxy_operation_benchmark_context.h index 27a3346..53bf0f1 100644 --- a/benchmarks/proxy_operation_benchmark_context.h +++ b/benchmarks/proxy_operation_benchmark_context.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/docs/faq.md b/docs/faq.md index e08ec82..5d48888 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -21,7 +21,7 @@ ### Why is "Proxy" so popular? -"Proxy" is built by engineers at Microsoft and initially deployed in the Windows operating system. For 40 years, the inheritance-based polymorphism paradigm has been the only scalable solution for runtime polymorphism in C++. However, a "virtual function" is no longer the optimal choice for runtime polymorphism today, and new languages with better paradigms, like [traits in Rust](https://doc.rust-lang.org/book/ch10-02-traits.html), are emerging. "Proxy" is our latest and greatest solution for generic runtime polymorphism in C++. It is easy to integrate and makes C++ feel like a brand new language when dealing with runtime abstractions. +"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). For 40 years, the inheritance-based polymorphism paradigm has been the only scalable solution for runtime polymorphism in C++. However, a "virtual function" is no longer the optimal choice for runtime polymorphism today, and new languages with better paradigms, like [traits in Rust](https://doc.rust-lang.org/book/ch10-02-traits.html), are emerging. "Proxy" is our latest and greatest solution for generic runtime polymorphism in C++. It is easy to integrate and makes C++ feel like a brand new language when dealing with runtime abstractions. ### Who is "Proxy" for? @@ -37,7 +37,7 @@ The fundamental abstraction of "Proxy" is called "facade". It is recommended for ### How to integrate "Proxy" into my project? -Since "Proxy" is a header-only 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#minimum-requirements-for-compilers). 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! +Since "Proxy" is a header-only library, you can simply navigate to the [latest release](https://github.com/ngcpp/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#minimum-requirements-for-compilers). 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"? @@ -92,4 +92,4 @@ These rules let old and new code coexist during the transition while keeping ODR ### What should I do if I found this library deficient in my scenario? -Please search for your scenario in the existing issues first, and feel free to file an a new one on demand, following the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +Please search for your scenario in the existing issues first, and feel free to file an a new one on demand, following the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). diff --git a/docs/resources/icon.png b/docs/resources/icon.png index dbfd434fc7cb6307acab0095cd97beb4f9491bdb..5b2ea194b6bd1185a6a13b467fb44929db8a1fa6 100644 GIT binary patch literal 28503 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 AhyVZp literal 2323 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn2Hfk$L90|U1(2s1Lwnj^u$ zz;V>m#WAE}&f9B-t7JkX+8)-oHD>Q|+2C>El#a^@z7p0vrKYb}xNbIf8!WxMHpY?5 zTib9uPx6kQGZ9`vuTC^p&QE9jUp|ZZJrB?Va>1YQ*`bG@|IOiFKi@WXEC2O3cMqR^ zcye|8{kTWh%I(iS?zf+L+xrfaU4eQ2x8Ig)XWUyWF?Xirp1N;eO78sImnFaM=GkvQ ziqiJ&J+julKds*GTsKhlz4AXrcC%;Nhvnpl_b@SJa{@gp#K0h+!oZ;5!NA}!fq|hx zfsuj15g1TSEDQ`RqY6gDVKhBZDPJtOyXs*%zj^!Yw;!Lbs@;*b{PW$Vez|Gy&P}&s z+w*+=;XgZn@e)y7T-X%3?a$-CkAG%tkKYC?7);7PpIyFQukh8M&o!pYE9*L=53tLn z?f+SIZylsmnECc+*}2b2H|=6Jzdds%Mup*-$@%oh>ks{ZRJLz^q%gy6(Zvi0qDHg) zXwf%X1< {% endblock %} diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 8c03e8b..b93727c 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index ca7fc85..0e66170 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_fmt_format_tests.cpp b/tests/proxy_fmt_format_tests.cpp index 10e1fb8..7467c23 100644 --- a/tests/proxy_fmt_format_tests.cpp +++ b/tests/proxy_fmt_format_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp index 8947973..4860b25 100644 --- a/tests/proxy_format_tests.cpp +++ b/tests/proxy_format_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index a9a6188..7f2fb6d 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 83350e9..5dd57ce 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 0a338a0..91942e8 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 5cc90b4..ecbf6cd 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 5ab2aa3..5c21917 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_rtti_tests.cpp b/tests/proxy_rtti_tests.cpp index 814c832..3965ca2 100644 --- a/tests/proxy_rtti_tests.cpp +++ b/tests/proxy_rtti_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index 8e81a4e..51ef045 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_view_tests.cpp b/tests/proxy_view_tests.cpp index 97dd1c9..9dbf3c5 100644 --- a/tests/proxy_view_tests.cpp +++ b/tests/proxy_view_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/utils.h b/tests/utils.h index f1835d2..8ad7fac 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #ifndef _MSFT_PROXY_TEST_UTILS_ diff --git a/tools/report_generator/main.cpp b/tools/report_generator/main.cpp index 3a0043a..9e47a46 100644 --- a/tools/report_generator/main.cpp +++ b/tools/report_generator/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) 2022-2026 Microsoft Corporation. // Licensed under the MIT License. #include From 3c49422e26861443d689648277a819956cd4515f Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 29 Jan 2026 17:06:24 +0800 Subject: [PATCH 11/14] Update email address (#2) --- CODE_OF_CONDUCT.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 97f59e1..191fcda 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,4 +6,4 @@ Resources: - [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) - [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) -- Contact [conduct@ngcpp.org](mailto:conduct@ngcpp.org) with questions or concerns +- Contact [ngcpp@outlook.com](mailto:ngcpp@outlook.com) with questions or concerns diff --git a/README.md b/README.md index 14be88c..75737c1 100644 --- a/README.md +++ b/README.md @@ -270,4 +270,4 @@ the rights to use your contribution. If a CLA is required, the PR bot will provi This project has adopted the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). For more information see the [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq/) or -contact [conduct@ngcpp.org](mailto:conduct@ngcpp.org) with any additional questions or comments. +contact [ngcpp@outlook.com](mailto:ngcpp@outlook.com) with any additional questions or comments. From 1a3f4646d5e3f8610d7338904677f7a0b9bff5b6 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 9 Mar 2026 16:02:22 +0800 Subject: [PATCH 12/14] Update copyright (#12) * Update copyright * Update license --- LICENSE | 1 + README.md | 2 +- benchmarks/proxy_creation_benchmark.cpp | 1 + benchmarks/proxy_operation_benchmark.cpp | 1 + .../proxy_operation_benchmark_context.cpp | 1 + .../proxy_operation_benchmark_context.h | 1 + docs/resources/icon.png | Bin 28503 -> 28984 bytes include/proxy/proxy.h | 1 + include/proxy/proxy_fmt.h | 1 + include/proxy/proxy_macros.h | 1 + include/proxy/v4/proxy.h | 1 + include/proxy/v4/proxy_fmt.h | 1 + include/proxy/v4/proxy_macros.h | 1 + mkdocs.yml | 2 +- tests/proxy_creation_tests.cpp | 1 + tests/proxy_dispatch_tests.cpp | 1 + tests/proxy_fmt_format_tests.cpp | 1 + tests/proxy_format_tests.cpp | 1 + tests/proxy_integration_tests.cpp | 1 + tests/proxy_invocation_tests.cpp | 1 + tests/proxy_lifetime_tests.cpp | 1 + tests/proxy_reflection_tests.cpp | 1 + tests/proxy_regression_tests.cpp | 1 + tests/proxy_rtti_tests.cpp | 1 + tests/proxy_traits_tests.cpp | 1 + tests/proxy_view_tests.cpp | 1 + tests/utils.h | 1 + tools/report_generator/main.cpp | 1 + 28 files changed, 27 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 84c4f3f..6f07338 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2022-2026 Microsoft Corporation. + Copyright (c) 2026-Present Next Gen C++ Foundation. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 75737c1..2b34a01 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ If so, this library is for you. "Proxy" is a modern C++ library that helps you use polymorphism (a way to use different types of objects interchangeably) without needing inheritance. -"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, and has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). This repository was ported from https://github.com/microsoft/proxy, where more historical releases can be found. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: +"Proxy" was created by Microsoft engineers and incubated at Microsoft from 2018 to Feb 2026, and has been used in the Windows operating system since 2022. It is now maintained by the Next Gen C++ Foundation (ngcpp). This repository was ported from [microsoft/proxy](https://github.com/microsoft/proxy), where more historical releases can be found. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like [Rust](https://doc.rust-lang.org/book/ch10-02-traits.html) offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use *pointers* in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be: - **Portable**: "Proxy" was implemented as a header-only library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is [freestanding](https://en.cppreference.com/w/cpp/freestanding), making it feasible for embedded engineering or kernel design of an operating system. - **Non-intrusive**: An implementation type is no longer required to inherit from an abstract binding. diff --git a/benchmarks/proxy_creation_benchmark.cpp b/benchmarks/proxy_creation_benchmark.cpp index 11d8b29..dc7ccda 100644 --- a/benchmarks/proxy_creation_benchmark.cpp +++ b/benchmarks/proxy_creation_benchmark.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/benchmarks/proxy_operation_benchmark.cpp b/benchmarks/proxy_operation_benchmark.cpp index dbb3053..9c21576 100644 --- a/benchmarks/proxy_operation_benchmark.cpp +++ b/benchmarks/proxy_operation_benchmark.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/benchmarks/proxy_operation_benchmark_context.cpp b/benchmarks/proxy_operation_benchmark_context.cpp index fedcef6..7ab293d 100644 --- a/benchmarks/proxy_operation_benchmark_context.cpp +++ b/benchmarks/proxy_operation_benchmark_context.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "proxy_operation_benchmark_context.h" diff --git a/benchmarks/proxy_operation_benchmark_context.h b/benchmarks/proxy_operation_benchmark_context.h index 53bf0f1..d0566c7 100644 --- a/benchmarks/proxy_operation_benchmark_context.h +++ b/benchmarks/proxy_operation_benchmark_context.h @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/docs/resources/icon.png b/docs/resources/icon.png index 5b2ea194b6bd1185a6a13b467fb44929db8a1fa6..bc874644edbd4955ce1e17058bdf1e809af97515 100644 GIT binary patch delta 28191 zcmYhiRX|kV_dcv*p@ft)2+~M*f9UR#?rxAA;HY$WgGe_>HwZ&_58Yiu4h{d~_xE1B z7vAWZy;tqEp7pFHZ25V^`g5`nB?U>;cLeXAJ$r^KEhVP%?Ah~#e}CS-2LFar$CfXK zjQTwq^w~2s=<{BOLeomsB_zQ1Oj=A>%|ria$-_x%gDp1CWL>S5+L;aLFcq_=idnVo_V0xL0(hu}sOo8>xbsNJB;B9=@%#LNinOwNemG zABto|#B&2!Cf@YbD zwgs#FN1C9fgZ@hB$2l93Tufw~nAjeH%6vdAEiR!W>aeyvSN`!xsi(`Tp2Wbk0y*X# zg_@X53b!(0GX!@zujUbIq}bLAN^O-#a;(FDFP!3lQ6wP(i06h^Dweh=*ie1DZIPBt z+4jmyW__5!(*Kj>FQtJ=N4^)~<2}t)Goh;7Ly6F49Xt7Dv=1p*;WTb+r4p-(QNob6 zS;UfkpFJR;)jVMFv{+TVjp+lmP{F&qE;h=~qN2Nw zY)u?sZ?R7HI=fhqD6NC}ONoIZW-VP*;P90H*_Jw-MS$8 z?1Vc}57ol_cdfx2j22mahQ|lOmxEMb^ESppHNMJ(k&sLx$o)r%bZ|L&7J1X6{L}og zYvBH|^87tR;94!LTR7RgTB9fP^YPGK=Hn^GFrw;1hP;UQw<7Sej|-(uKhaE`wMB?zDEx34a*&R3kvQoAF<7mO)8t zlKg{lk#R8G>*Wl5%0dANYASizTeZ&&2yyCIB@8DvM96f3<%I`!(wt59c}l)-Z&o<% zqysVHpSw$?3WJlIkH!<0pb8uIuiF2>#9)&rPr4ZAKYHQZMwzWtU2EVWEUw_?LhAGB z>CWKg;UQXiH7slWIzdlReXMoIe`hq9d96{>SqlaOMkw_#u@`K^B1g=}Ue1Rn?njhl zyr4^n5^;4w@d8rAg&iRBxzFN^BYs_P+I=<$qmL*Q z@DH`DP9rsNMGN#D`^yaqSk&8f|5A17a?0+rAQ)iWACPgJr0NChj18yO8c46(TB#;F z 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%0A{WqRaT6=J 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�Q|UqRQ;cg@DBTVXj^Jkf|XrQwz+@) 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 z zy_OXJfLjb22tH4NDEfwL$~$8lcIJv=M7UUVx;Z`bu_^KtLK#?7Lya}mg1B%c$8>W# z4@}v{0%_w+1MZ%MS_->VftuBJEiHVA6|JZ_Ns<~tx*q!ITrq?TTXffNU?ZN&U==iT zB*8WkBX1O21dLM?WYnh(MiXq6EvHV>8-<% z!p)NRF@!p!9hipVVJ1;v52$2o@svad^KFg640o_30zhT^YWSY{lLyEj3&sI7(cNA} zPCYuYGiA$-NXlg(4b8ePCUbmnl5^<*$L^?nMycy;J-|DI%G*_f=fOac8pNOR8)Ifx z)0QzeKb&S~C_Qr+1;P!gMJ2FW(Vv_3_ylSlVf3DBKnMg4DXs z8F{Oc#v+C8Ei+^u-K35|P;M?*qe9^6-YZ1a0|l=P)@~B&!)5~)I>ST|zY-ioV}-k270N%@;*x-C#F|isw@A;Lah2SXFD*bcUgOYJ{#M@V<*qF| zGZ!==gf{!<)D9@cLDhY{=Fj&@r57z8;&3ZTHw38%sTd4=FsrF13^@K3NoJfmr0_?T zW8TO0XE{$zKO=P60n{hmp`t4~I=QzrLIk-2CscsI$`DNyD~dBU=~D`ZXxGMGZrbzz zN|{tWEus3bbetLZ0FqXZirXv2vI$UkA3xpyUhDY+eqk(cDu+HZ%ZvZ^7O=U?X3eRY z22aBfa!D3|GiY0gwur77+!(?iw&>&P^b$H&!A7tW&~M<$u}KR{Wtp-wQ$Yu_X{~E5 zdNaYhOfFc!g^^t1053EEIdlxBz8F#tjVK0>I$L*{&(3z(`a%;w3R%cp7KUgOB%}CK zlN%X~5g+(dhHs`}YJ_~j+T{G}pU>mh8SwzKERe)1ILY`WYi!NUDIe&ArooyqydyVy z*R=9zsGybBQ9`*jKuZ1$DJ54XC3hEF)Ij#^NEswSlIjc&HFC}8qHj0@7!|D@cu9zU zTTG+t+jf*qqJ1&Qd3FR&RiYmO+r6OiFz8sB6hKBd7B=tSe_Z8nU%14y6g;a2sOhM! zCh9pxP-JW|iN+Pc6O3zNo}b+>P0{u3Ak5tVD#{n^6K{A%h_?Rm_gbZkb_WDvB_ww4 z)XiZU^gZtyk8L}5Nwo~c=)B}czM$C?_zZPZx+2%n3Vepl;7KeqDE2R@#Q7_eS5i^D zsb&#LWKdznNeRwW!omgs?9rip*Rb)+pY>_>_IC9M*&`bl|5v35oJU(@w3Lc3$AuiG z6K!1ei<`bu>r1-AZz@Jr7n8x&qq2tGEucH{OTnz!Wl)$UB=@Io+_qnVZeTG4ED;1A z4W5ir%CQ-ZMe0Y=5qbea4_HraiB>^SwZ)axskL8#*#qk@*EphWx2-s$@0+ml+`=xs zy%%=K6&{rsdx7zb??P|lTc*Di+>=PDSU+7gKo=7MR*B+<37x+z@F8#Rf$f{H;GW?T z7glkT5Egy~t0%o2x*?NzcB&gbrb5xDBsP#hRs}lNNxf;xdVD?bWUO!s^5a&oCopyright (c) 2026-Present Next Gen C++ Foundation repo_url: https://github.com/ngcpp/proxy/ edit_uri: "" diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index b93727c..d88dc24 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index 0e66170..afdbf87 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_fmt_format_tests.cpp b/tests/proxy_fmt_format_tests.cpp index 7467c23..7a4a93e 100644 --- a/tests/proxy_fmt_format_tests.cpp +++ b/tests/proxy_fmt_format_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_format_tests.cpp b/tests/proxy_format_tests.cpp index 4860b25..848cef0 100644 --- a/tests/proxy_format_tests.cpp +++ b/tests/proxy_format_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index 7f2fb6d..d54d415 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 5dd57ce..69eacd0 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 91942e8..24d5e49 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index ecbf6cd..ab24402 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 5c21917..443af72 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_rtti_tests.cpp b/tests/proxy_rtti_tests.cpp index 3965ca2..1545939 100644 --- a/tests/proxy_rtti_tests.cpp +++ b/tests/proxy_rtti_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index 51ef045..0f092e6 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/proxy_view_tests.cpp b/tests/proxy_view_tests.cpp index 9dbf3c5..f47f5b0 100644 --- a/tests/proxy_view_tests.cpp +++ b/tests/proxy_view_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include "utils.h" diff --git a/tests/utils.h b/tests/utils.h index 8ad7fac..000b356 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #ifndef _MSFT_PROXY_TEST_UTILS_ diff --git a/tools/report_generator/main.cpp b/tools/report_generator/main.cpp index 9e47a46..a83dcde 100644 --- a/tools/report_generator/main.cpp +++ b/tools/report_generator/main.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2022-2026 Microsoft Corporation. +// Copyright (c) 2026-Present Next Gen C++ Foundation. // Licensed under the MIT License. #include From 3ef800172fdff0f3f8294ccdb6ee84cbcea7709c Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 12 Mar 2026 23:27:40 +0800 Subject: [PATCH 13/14] Fix `weak_dispatch` and `explicit_conversion_dispatch` in reference-returning overloads (#13) * Fix explicit_conversion_dispatch and weak_dispatch for reference-returning overloads * Resolve comments --- include/proxy/v4/proxy.h | 45 +++++++++++++++++++++++++++----- tests/CMakeLists.txt | 1 + tests/proxy_details_tests.cpp | 31 ++++++++++++++++++++++ tests/proxy_dispatch_tests.cpp | 8 +++--- tests/proxy_regression_tests.cpp | 23 ++++++++++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 tests/proxy_details_tests.cpp diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index ac1408c..ae9903f 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -1289,10 +1289,26 @@ struct converter { template operator T() && noexcept( std::is_nothrow_invocable_r_v>) - requires(std::is_invocable_r_v>) + requires(std::is_invocable_r_v> && + !std::is_invocable_r_v> && + !std::is_invocable_r_v>) { return std::move(f_)(std::in_place_type); } + template + operator T&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } + template + operator T&&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } private: F f_; @@ -2347,14 +2363,29 @@ struct sign { template sign(const char (&str)[N]) -> sign; -struct wildcard { - wildcard() = delete; +// When std::reference_constructs_from_temporary_v (C++23) is not available, we +// fall back to a conservative approximation that disallows binding a temporary +// to a reference type if the source type is not a reference or if the source +// and target reference types are not compatible. +template +concept explicitly_convertible = + std::is_constructible_v && +#if __cpp_lib_reference_from_temporary >= 202202L + !std::reference_constructs_from_temporary_v; +#else + (!std::is_reference_v || + (std::is_reference_v && + std::is_convertible_v>, + std::add_pointer_t>>)); +#endif // __cpp_lib_reference_from_temporary >= 202202L +struct noreturn_conversion { template - [[noreturn]] operator T() const { + [[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t) { PROD_UNREACHABLE(); } }; +using wildcard = converter; } // namespace details @@ -2578,9 +2609,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base { PRO4D_STATIC_CALL(auto, T&& self) noexcept { return details::converter{ [&self](std::in_place_type_t) noexcept( - std::is_nothrow_constructible_v) - requires(std::is_constructible_v) - { return U{std::forward(self)}; }}; + std::is_nothrow_constructible_v) -> U + requires(details::explicitly_convertible < T &&, U >) + { return static_cast(std::forward(self)); }}; } }; using conversion_dispatch = explicit_conversion_dispatch; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 127c21b..ff508a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ include(GoogleTest) add_executable(msft_proxy_tests proxy_creation_tests.cpp + proxy_details_tests.cpp proxy_dispatch_tests.cpp proxy_fmt_format_tests.cpp proxy_format_tests.cpp diff --git a/tests/proxy_details_tests.cpp b/tests/proxy_details_tests.cpp new file mode 100644 index 0000000..a10cd4f --- /dev/null +++ b/tests/proxy_details_tests.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2026-Present Next Gen C++ Foundation. +// Licensed under the MIT License. + +#include + +namespace proxy_details_tests_details { + +struct Base { + int v; +}; +struct Derived : Base {}; + +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert( + pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); + +} // namespace proxy_details_tests_details diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index afdbf87..1dde852 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -784,11 +784,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) { TEST(ProxyDispatchTests, TestIndirectConversion) { struct TestFacade - : pro::facade_builder::add_convention::build {}; + : pro::facade_builder // + ::add_convention // + ::build {}; short v = 123; pro::proxy p = &v; - ASSERT_EQ(static_cast(*p), 123); + static_cast(*p) = 456; + ASSERT_EQ(static_cast(*p), 456); } TEST(ProxyDispatchTests, TestDirectConversion) { diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 443af72..d338908 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -3,7 +3,15 @@ // Licensed under the MIT License. #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning( \ + disable : 4702) // False alarm from MSVC: warning C4702: unreachable code +#endif // defined(_MSC_VER) && !defined(__clang__) #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__clang__) #include namespace proxy_regression_tests_details { @@ -40,6 +48,8 @@ struct Range : pro::facade_builder // ::template add_convention>()> // ::build {}; +PRO_DEF_MEM_DISPATCH(MemFun, Fun); + } // namespace proxy_regression_tests_details namespace details = proxy_regression_tests_details; @@ -70,3 +80,16 @@ TEST(ProxyRegressionTests, TestProxiableSelfDependency) { } EXPECT_EQ(expected, original); } + +// https://github.com/ngcpp/proxy/issues/10 +TEST(ProxyRegressionTests, TestWeakDispatchReferenceReturningOverload) { + struct MyFacade + : pro::facade_builder // + ::add_convention, int&()> // + ::build {}; + static_assert(pro::proxiable); + + int v = 123; + pro::proxy p = &v; + EXPECT_THROW(p->Fun(), pro::not_implemented); +} From bcbe0c792cd42cb48edfbbdb1e272255cbe73cae Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Fri, 13 Mar 2026 22:18:49 +0800 Subject: [PATCH 14/14] Update version to 4.0.2 (#14) --- CMakeLists.txt | 2 +- docs/spec/msft_lib_proxy.md | 1 + include/proxy/v4/proxy_macros.h | 2 +- meson.build | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae1ffdc..60e796c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.28) -project(msft_proxy4 VERSION 4.0.1 LANGUAGES CXX) +project(msft_proxy4 VERSION 4.0.2 LANGUAGES CXX) add_library(msft_proxy4 INTERFACE) set_target_properties(msft_proxy4 PROPERTIES EXPORT_NAME proxy) add_library(msft_proxy4::proxy ALIAS msft_proxy4) diff --git a/docs/spec/msft_lib_proxy.md b/docs/spec/msft_lib_proxy.md index b3c8593..71612e6 100644 --- a/docs/spec/msft_lib_proxy.md +++ b/docs/spec/msft_lib_proxy.md @@ -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.2 | `202603L` | | 4.0.1 | `202510L` | | 4.0.0 | `202508L` | | 3.4.0 | `202505L` | diff --git a/include/proxy/v4/proxy_macros.h b/include/proxy/v4/proxy_macros.h index 61a2d49..74ad2f4 100644 --- a/include/proxy/v4/proxy_macros.h +++ b/include/proxy/v4/proxy_macros.h @@ -29,7 +29,7 @@ #define PRO4D_DEBUG(...) __VA_ARGS__ #endif // NDEBUG -#define __msft_lib_proxy4 202510L +#define __msft_lib_proxy4 202603L #define PRO4D_DIRECT_FUNC_IMPL(...) \ noexcept(noexcept(__VA_ARGS__)) \ diff --git a/meson.build b/meson.build index d909e1a..5905e86 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'msft_proxy4', 'cpp', - version: '4.0.1', + version: '4.0.2', license: 'MIT', license_files: 'LICENSE', meson_version: '>=1.3',