From b1c8247ecb7717d1732a83aad66d54befe41dbac Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Tue, 19 Dec 2023 21:37:28 +0800 Subject: [PATCH 1/6] Prepare for V2 implementation --- proxy.h | 162 +++++++++++++++++------------- tests/proxy_creation_tests.cpp | 17 ++-- tests/proxy_integration_tests.cpp | 2 +- tests/proxy_invocation_tests.cpp | 13 +-- tests/proxy_lifetime_tests.cpp | 4 +- tests/proxy_reflection_tests.cpp | 8 +- tests/proxy_traits_tests.cpp | 43 ++++---- 7 files changed, 134 insertions(+), 115 deletions(-) diff --git a/proxy.h b/proxy.h index 6f6cbff..38501ad 100644 --- a/proxy.h +++ b/proxy.h @@ -16,6 +16,35 @@ namespace pro { enum class constraint_level { none, nontrivial, nothrow, trivial }; +struct proxy_pointer_constraints { + std::size_t maximum_size; + std::size_t maximum_alignment; + constraint_level minimum_copyability; + constraint_level minimum_relocatability; + constraint_level minimum_destructibility; +}; +constexpr proxy_pointer_constraints relocatable_pointer_constraints{ + .maximum_size = sizeof(void*) * 2u, + .maximum_alignment = alignof(void*), + .minimum_copyability = constraint_level::none, + .minimum_relocatability = constraint_level::nothrow, + .minimum_destructibility = constraint_level::nothrow, +}; +constexpr proxy_pointer_constraints copyable_pointer_constraints{ + .maximum_size = sizeof(void*) * 2u, + .maximum_alignment = alignof(void*), + .minimum_copyability = constraint_level::nontrivial, + .minimum_relocatability = constraint_level::nothrow, + .minimum_destructibility = constraint_level::nothrow, +}; +constexpr proxy_pointer_constraints trivial_pointer_constraints{ + .maximum_size = sizeof(void*), + .maximum_alignment = alignof(void*), + .minimum_copyability = constraint_level::trivial, + .minimum_relocatability = constraint_level::trivial, + .minimum_destructibility = constraint_level::trivial, +}; + namespace details { struct applicable_traits { static constexpr bool applicable = true; }; @@ -153,8 +182,8 @@ template requires(requires { typename D::overload_types; { D{} }; }) -struct dispatch_traits : dispatch_traits_impl< - D, typename flattening_traits::type> {}; +struct dispatch_traits + : dispatch_traits_impl {}; template struct dispatch_meta { @@ -217,11 +246,14 @@ template <> struct facade_meta_traits<> { using type = facade_meta<>; }; template struct basic_facade_traits_impl; template -struct basic_facade_traits_impl> : applicable_traits { +struct basic_facade_traits_impl> { using meta_type = typename facade_meta_traits< - conditional_meta_tag, - conditional_meta_tag, - conditional_meta_tag, + conditional_meta_tag< + F::pointer_constraints.minimum_copyability, copy_meta>, + conditional_meta_tag< + F::pointer_constraints.minimum_relocatability, relocation_meta>, + conditional_meta_tag< + F::pointer_constraints.minimum_destructibility, destruction_meta>, conditional_meta_tag ? constraint_level::none : constraint_level::nothrow, typename F::reflection_type>>::type; @@ -230,19 +262,8 @@ struct basic_facade_traits_impl> : applicable_traits { template static constexpr bool has_dispatch = contains_traits::applicable; }; -template struct basic_facade_traits : inapplicable_traits {}; -template requires(requires { - typename F::dispatch_types; - typename F::reflection_type; - typename std::integral_constant; - typename std::integral_constant; - typename std::integral_constant; - typename std::integral_constant< - constraint_level, F::minimum_relocatability>; - typename std::integral_constant< - constraint_level, F::minimum_destructibility>; - }) -struct basic_facade_traits : basic_facade_traits_impl< +template +struct basic_facade_traits : basic_facade_traits_impl< F, typename flattening_traits::type> {}; template @@ -254,10 +275,11 @@ struct facade_traits_impl> : applicable_traits { template static constexpr bool applicable_pointer = - sizeof(P) <= F::maximum_size && alignof(P) <= F::maximum_alignment && - has_copyability

(F::minimum_copyability) && - has_relocatability

(F::minimum_relocatability) && - has_destructibility

(F::minimum_destructibility) && + sizeof(P) <= F::pointer_constraints.maximum_size && + alignof(P) <= F::pointer_constraints.maximum_alignment && + has_copyability

(F::pointer_constraints.minimum_copyability) && + has_relocatability

(F::pointer_constraints.minimum_relocatability) && + has_destructibility

(F::pointer_constraints.minimum_destructibility) && (dispatch_traits::template applicable_pointer

&& ...) && (std::is_void_v || std::is_constructible_v< typename F::reflection_type, std::in_place_type_t

>); @@ -272,13 +294,20 @@ using dependent_t = typename dependent_traits::type; } // namespace details +template +concept facade = requires { + typename F::dispatch_types; + typename std::integral_constant< + proxy_pointer_constraints, F::pointer_constraints>; + typename F::reflection_type; +}; + template -concept proxiable = details::is_address_deducible && - details::basic_facade_traits::applicable && +concept proxiable = facade && details::is_address_deducible && details::facade_traits::applicable && details::facade_traits::template applicable_pointer

; -template requires(details::basic_facade_traits::applicable) +template class proxy { using BasicTraits = details::basic_facade_traits; using Traits = details::facade_traits; @@ -293,21 +322,21 @@ class proxy { proxiable, std::is_constructible, std::false_type>::value; static constexpr bool HasTrivialCopyConstructor = - F::minimum_copyability == constraint_level::trivial; + F::pointer_constraints.minimum_copyability == constraint_level::trivial; static constexpr bool HasNothrowCopyConstructor = - F::minimum_copyability >= constraint_level::nothrow; - static constexpr bool HasCopyConstructor = - F::minimum_copyability >= constraint_level::nontrivial; - static constexpr bool HasNothrowMoveConstructor = - F::minimum_relocatability >= constraint_level::nothrow; - static constexpr bool HasMoveConstructor = - F::minimum_relocatability >= constraint_level::nontrivial; - static constexpr bool HasTrivialDestructor = - F::minimum_destructibility == constraint_level::trivial; - static constexpr bool HasNothrowDestructor = - F::minimum_destructibility >= constraint_level::nothrow; - static constexpr bool HasDestructor = - F::minimum_destructibility >= constraint_level::nontrivial; + F::pointer_constraints.minimum_copyability >= constraint_level::nothrow; + static constexpr bool HasCopyConstructor = F::pointer_constraints + .minimum_copyability >= constraint_level::nontrivial; + static constexpr bool HasNothrowMoveConstructor = F::pointer_constraints + .minimum_relocatability >= constraint_level::nothrow; + static constexpr bool HasMoveConstructor = F::pointer_constraints + .minimum_relocatability >= constraint_level::nontrivial; + static constexpr bool HasTrivialDestructor = F::pointer_constraints + .minimum_destructibility == constraint_level::trivial; + static constexpr bool HasNothrowDestructor = F::pointer_constraints + .minimum_destructibility >= constraint_level::nothrow; + static constexpr bool HasDestructor = F::pointer_constraints + .minimum_destructibility >= constraint_level::nontrivial; template static constexpr bool HasNothrowPolyAssignment = HasNothrowPolyConstructor && HasNothrowDestructor; @@ -341,8 +370,9 @@ class proxy { proxy(proxy&& rhs) noexcept(HasNothrowMoveConstructor) requires(HasMoveConstructor) { if (rhs.meta_ != nullptr) { - if constexpr (F::minimum_relocatability == constraint_level::trivial) { - memcpy(ptr_, rhs.ptr_, F::maximum_size); + if constexpr (F::pointer_constraints.minimum_relocatability == + constraint_level::trivial) { + memcpy(ptr_, rhs.ptr_, F::pointer_constraints.maximum_size); } else { rhs.meta_->relocate(ptr_, rhs.ptr_); } @@ -428,7 +458,8 @@ class proxy { { this->~proxy(); meta_ = nullptr; } void swap(proxy& rhs) noexcept(HasNothrowMoveConstructor) requires(HasMoveConstructor) { - if constexpr (F::minimum_relocatability == constraint_level::trivial) { + if constexpr (F::pointer_constraints.minimum_relocatability == + constraint_level::trivial) { std::swap(meta_, rhs.meta_); std::swap(ptr_, rhs.ptr); } else { @@ -491,7 +522,8 @@ class proxy { } const typename BasicTraits::meta_type* meta_; - alignas(F::maximum_alignment) char ptr_[F::maximum_size]; + alignas(F::pointer_constraints.maximum_alignment) + char ptr_[F::pointer_constraints.maximum_size]; }; namespace details { @@ -551,26 +583,24 @@ proxy make_proxy(T&& value) { return details::make_proxy_impl>(std::forward(value)); } -template -struct dispatch { using overload_types = std::tuple; }; - -template -struct facade { - using dispatch_types = std::tuple; - using reflection_type = void; - static constexpr std::size_t maximum_size = sizeof(void*) * 2u; - static constexpr std::size_t maximum_alignment = alignof(void*); - static constexpr auto minimum_copyability = constraint_level::none; - static constexpr auto minimum_relocatability = constraint_level::nothrow; - static constexpr auto minimum_destructibility = constraint_level::nothrow; - facade() = delete; +// The following types and macros aim to simplify definition of dispatch and +// facade types before C++26 +namespace helper { + +template , proxy_pointer_constraints C = + relocatable_pointer_constraints, class R = void> +struct facade_prototype { + using dispatch_types = D; + static constexpr proxy_pointer_constraints pointer_constraints = C; + using reflection_type = R; }; -} // namespace pro +} // namespace helper -// The following macros facilitate definition of dispatch and facade types +} // namespace pro #define DEFINE_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ - struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ + struct __NAME { \ + using overload_types = std::tuple<__VA_ARGS__>;\ template \ decltype(auto) operator()(__T&& __self, __Args&&... __args) \ requires(requires{ std::forward<__T>(__self) \ @@ -580,7 +610,8 @@ struct facade { } \ } #define DEFINE_FREE_DISPATCH(__NAME, __FUNC, ...) \ - struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ + struct __NAME { \ + using overload_types = std::tuple<__VA_ARGS__>;\ template \ decltype(auto) operator()(__T&& __self, __Args&&... __args) \ requires(requires{ __FUNC(std::forward<__T>(__self), \ @@ -589,13 +620,8 @@ struct facade { std::forward<__Args>(__args)...); \ } \ } - +#define MAKE_DISPATCH_PACK(...) std::tuple<__VA_ARGS__> #define DEFINE_FACADE(__NAME, ...) \ - struct __NAME : ::pro::facade<__VA_ARGS__> {} -#define DEFINE_COPYABLE_FACADE(__NAME, ...) \ - struct __NAME : ::pro::facade<__VA_ARGS__> { \ - static constexpr auto minimum_copyability = \ - pro::constraint_level::nontrivial; \ - } + struct __NAME : ::pro::helper::facade_prototype<__VA_ARGS__> {} #endif // _MSFT_PROXY_ diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 9e140e9..33eac93 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -21,15 +21,14 @@ struct SboObserver { bool SboEnabled; }; -struct TestSmallStringable : pro::facade { - using reflection_type = SboObserver; - static constexpr std::size_t maximum_size = sizeof(void*); - static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; -}; -struct TestLargeStringable : pro::facade { - using reflection_type = SboObserver; - static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; -}; +DEFINE_FACADE(TestSmallStringable, utils::poly::ToString, pro::proxy_pointer_constraints{ + .maximum_size = sizeof(void*), + .maximum_alignment = alignof(void*), + .minimum_copyability = pro::constraint_level::nontrivial, + .minimum_relocatability = pro::constraint_level::nothrow, + .minimum_destructibility = pro::constraint_level::nothrow, + }, SboObserver); +DEFINE_FACADE(TestLargeStringable, utils::poly::ToString, pro::copyable_pointer_constraints, SboObserver); } // namespace poly diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index 5ab07f9..782d9ba 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -17,7 +17,7 @@ namespace poly { DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(Drawable, Draw, Area); +DEFINE_FACADE(Drawable, MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index f37999e..b98d688 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -18,20 +18,22 @@ namespace poly { template DEFINE_MEMBER_DISPATCH(Call, operator(), Os...); template -DEFINE_COPYABLE_FACADE(Callable, Call); +DEFINE_FACADE(Callable, Call, pro::copyable_pointer_constraints); DEFINE_FREE_DISPATCH(GetSize, std::ranges::size, std::size_t()); template DEFINE_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); template -DEFINE_FACADE(Iterable, ForEach, GetSize); +DEFINE_FACADE(Iterable, MAKE_DISPATCH_PACK(ForEach, GetSize)); template struct Append; template -DEFINE_FACADE(Container, ForEach, GetSize, Append); +DEFINE_FACADE(Container, MAKE_DISPATCH_PACK(ForEach, GetSize, Append)); template -struct Append : pro::dispatch>(T)> { +struct Append { + using overload_types = std::tuple>(T)>; + template pro::proxy> operator()(U& self, T&& value) { self.push_back(std::move(value)); @@ -115,8 +117,7 @@ TEST(ProxyInvocationTests, TestMultipleDispatches_Unique) { TEST(ProxyInvocationTests, TestMultipleDispatches_Duplicated) { using SomeCombination = std::tuple, std::tuple>>; - struct DuplicatedIterable : pro::facade< - poly::ForEach, SomeCombination, poly::ForEach, poly::GetSize, poly::GetSize> {}; + DEFINE_FACADE(DuplicatedIterable, MAKE_DISPATCH_PACK(poly::ForEach, SomeCombination, poly::ForEach, poly::GetSize, poly::GetSize)); static_assert(sizeof(pro::details::facade_traits::meta_type) == sizeof(pro::details::facade_traits>::meta_type)); std::list l = { 1, 2, 3 }; diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 994ac1c..7dad605 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -7,9 +7,7 @@ namespace { -struct TestFacade : pro::facade { - static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; -}; +DEFINE_FACADE(TestFacade, utils::poly::ToString, pro::copyable_pointer_constraints); } // namespace diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 971e312..4d3fa62 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -42,15 +42,13 @@ struct TraitsReflection { bool is_trivial_; }; -using DefaultFacade = pro::facade<>; +DEFINE_FACADE(DefaultFacade); static_assert(!ReflectionApplicable); -struct TestRttiFacade : pro::facade<> - { using reflection_type = RttiReflection; }; +DEFINE_FACADE(TestRttiFacade, MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, RttiReflection); static_assert(ReflectionApplicable); -struct TestTraitsFacade : pro::facade<> - { using reflection_type = TraitsReflection; }; +DEFINE_FACADE(TestTraitsFacade, MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, TraitsReflection); static_assert(ReflectionApplicable); } // namespace diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index f8c2a2a..f807d37 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -21,14 +21,14 @@ struct MockPtr { using MockMovablePtr = MockPtr; using MockCopyablePtr = MockPtr; using MockCopyableSmallPtr = MockPtr; -using MockTrivialPtr = MockPtr; +using MockTrivialPtr = MockPtr; -using DefaultFacade = pro::facade<>; -static_assert(DefaultFacade::minimum_copyability == pro::constraint_level::none); -static_assert(DefaultFacade::minimum_relocatability == pro::constraint_level::nothrow); -static_assert(DefaultFacade::minimum_destructibility == pro::constraint_level::nothrow); -static_assert(DefaultFacade::maximum_size >= 2 * sizeof(void*)); -static_assert(DefaultFacade::maximum_alignment >= sizeof(void*)); +DEFINE_FACADE(DefaultFacade); +static_assert(DefaultFacade::pointer_constraints.minimum_copyability == pro::constraint_level::none); +static_assert(DefaultFacade::pointer_constraints.minimum_relocatability == pro::constraint_level::nothrow); +static_assert(DefaultFacade::pointer_constraints.minimum_destructibility == pro::constraint_level::nothrow); +static_assert(DefaultFacade::pointer_constraints.maximum_size >= 2 * sizeof(void*)); +static_assert(DefaultFacade::pointer_constraints.maximum_alignment >= sizeof(void*)); static_assert(std::is_same_v>); static_assert(std::is_same_v); static_assert(std::is_nothrow_default_constructible_v>); @@ -40,7 +40,7 @@ static_assert(std::is_nothrow_constructible_v, std::in static_assert(std::is_nothrow_constructible_v, std::in_place_type_t, int>); static_assert(std::is_nothrow_constructible_v, std::in_place_type_t, int>); -struct RelocatableFacade : pro::facade<> {}; +DEFINE_FACADE(RelocatableFacade); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_nothrow_move_constructible_v>); @@ -64,9 +64,7 @@ static_assert(std::is_nothrow_assignable_v, MockCo static_assert(std::is_nothrow_constructible_v, MockTrivialPtr>); static_assert(std::is_nothrow_assignable_v, MockTrivialPtr>); -struct CopyableFacade : pro::facade<> { - static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; -}; +DEFINE_FACADE(CopyableFacade, MAKE_DISPATCH_PACK(), pro::copyable_pointer_constraints); static_assert(std::is_copy_constructible_v>); static_assert(!std::is_nothrow_copy_constructible_v>); static_assert(std::is_copy_assignable_v>); @@ -90,28 +88,27 @@ static_assert(std::is_nothrow_assignable_v, MockCopya static_assert(std::is_nothrow_constructible_v, MockTrivialPtr>); static_assert(std::is_nothrow_assignable_v, MockTrivialPtr>); -struct CopyableSmallFacade : pro::facade<> { - static constexpr std::size_t maximum_size = sizeof(void*); - static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; -}; +DEFINE_FACADE(CopyableSmallFacade, MAKE_DISPATCH_PACK(), pro::proxy_pointer_constraints{ + .maximum_size = sizeof(void*), + .maximum_alignment = alignof(void*), + .minimum_copyability = pro::constraint_level::nontrivial, + .minimum_relocatability = pro::constraint_level::nothrow, + .minimum_destructibility = pro::constraint_level::nothrow, + }); static_assert(!pro::proxiable); static_assert(!pro::proxiable); static_assert(pro::proxiable); -static_assert(!pro::proxiable); +static_assert(pro::proxiable); static_assert(!std::is_constructible_v, MockMovablePtr>); static_assert(!std::is_assignable_v, MockMovablePtr>); static_assert(!std::is_constructible_v, MockCopyablePtr>); static_assert(!std::is_assignable_v, MockCopyablePtr>); static_assert(std::is_nothrow_constructible_v, MockCopyableSmallPtr>); static_assert(std::is_nothrow_assignable_v, MockCopyableSmallPtr>); -static_assert(!std::is_constructible_v, MockTrivialPtr>); -static_assert(!std::is_assignable_v, MockTrivialPtr>); +static_assert(std::is_constructible_v, MockTrivialPtr>); +static_assert(std::is_assignable_v, MockTrivialPtr>); -struct TrivialFacade : pro::facade<> { - static constexpr auto minimum_copyability = pro::constraint_level::trivial; - static constexpr auto minimum_relocatability = pro::constraint_level::trivial; - static constexpr auto minimum_destructibility = pro::constraint_level::trivial; -}; +DEFINE_FACADE(TrivialFacade, MAKE_DISPATCH_PACK(), pro::trivial_pointer_constraints); static_assert(std::is_trivially_copy_constructible_v>); static_assert(std::is_trivially_copy_assignable_v>); static_assert(std::is_nothrow_move_constructible_v>); From 7c6bbba346a9d8fe6e0d3cd8d6a26231e5b05cfb Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Tue, 19 Dec 2023 22:55:28 +0800 Subject: [PATCH 2/6] Update README.md --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8757fdd..5e20ad5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Proxy: Easy Polymorphism in C++ +# Proxy: Redefine 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) @@ -10,13 +10,15 @@ Have you tried other polymorphic programming libraries in C++ but found them def If so, this library is for you. 😉 +For decades, object-based virtual table has been a de facto implementation of runtime polymorphism in many (compiled) programming languages. There are many drawbacks in this mechanism, including life management (because each object may have different size and ownership) and reflection (because it is hard to balance between usability and memory allocation). To workaround these drawbacks, some languages like Java or C# choose to sacrifice performance by introducing GC to facilitate lifetime management, and JIT-compile the source code at runtime to generate full metadata. We improved the theory and implemented as a C++ library without sacrificing performance, proposed to merge into the C++ standard. + The "proxy" is a single-header, cross-platform C++ library that Microsoft uses to make runtime polymorphism easier to implement and faster. Please find the design details at https://wg21.link/p0957. ## Quick start The "proxy" is a header-only C++20 library. Once you set the language level of your compiler not earlier than C++20 and get the header file ([proxy.h](proxy.h)), you are all set. You can also install the library via [vcpkg](https://github.com/microsoft/vcpkg/), which is a C++ library manager invented by Microsoft, by searching for "proxy" (see [vcpkg.info](https://vcpkg.info/port/proxy)). -All the facilities of the library are defined in namespace `pro`. The 3 major class templates are `dispatch`, `facade` and `proxy`. Some macros are defined (currently not in the proposal of standardization) to facilitate definition of `dispatch`es and `facade`s. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach: +The majority of the library is defined in namespace `pro`. Some macros are provided (currently not included in the proposal of standardization) to simplify the definiton of `proxy` prior to C++26. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach: ```cpp // Abstraction (poly is short for polymorphism) @@ -24,7 +26,7 @@ namespace poly { DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(Drawable, Draw, Area); +DEFINE_FACADE(Drawable, MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly @@ -60,6 +62,46 @@ pro::proxy CreateRectangleAsDrawable(int width, int height) { } ``` +Here is another demo showing how to define overloads in a dispatch. Note that `.invoke<>` can be ommitted when only 1 dispatch is defined in a facade: + +```cpp +// Abstraction (poly is short for polymorphism) +namespace poly { + +DEFINE_MEMBER_DISPATCH(Log, Log, + void(const char*), void(const char*, const std::exception&)); +DEFINE_FACADE(Logger, Log); + +} // namespace poly + +// Client - Consumer +void MyVerboseFunction(pro::proxy logger) { + logger("hello"); + try { + throw std::runtime_error{"runtime error!"}; + } catch (const std::exception& e) { + logger("world", e); + } +} + +// Implementation +struct MyLogger { + void Log(const char* s) { + printf("[INFO] %s\n", s); + } + void Log(const char* s, const std::exception& e) { + printf("[ERROR] %s (exception info: %s)\n", s, e.what()); + } +}; + +// Client - Producer +int main() { + MyLogger logger; + MyVerboseFunction(&logger); + return 0; +} +``` + Please find more details and discussions in the spec. The complete version of the "drawable" demo could be found in [tests/proxy_integration_tests.cpp](tests/proxy_integration_tests.cpp) (also available on [Compiler Explorer](https://godbolt.org/z/5a3jeE1M8)). ## Minimum requirements for compilers From 2e24851c1e73610b60e9b4ef2acd7ea42ad74441 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Tue, 19 Dec 2023 23:23:47 +0800 Subject: [PATCH 3/6] Resolve comments --- README.md | 10 +++++----- proxy.h | 11 ++++++----- samples/resource_dictionary/main.cpp | 4 ++-- tests/proxy_creation_tests.cpp | 4 ++-- tests/proxy_integration_tests.cpp | 6 +++--- tests/proxy_invocation_tests.cpp | 14 +++++++------- tests/proxy_lifetime_tests.cpp | 2 +- tests/proxy_reflection_tests.cpp | 6 +++--- tests/proxy_traits_tests.cpp | 10 +++++----- tests/utils.h | 2 +- 10 files changed, 35 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 5e20ad5..12ec6f9 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ The majority of the library is defined in namespace `pro`. Some macros are provi // Abstraction (poly is short for polymorphism) namespace poly { -DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); -DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(Drawable, MAKE_DISPATCH_PACK(Draw, Area)); +PRO_DEF_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); +PRO_DEF_MEMBER_DISPATCH(Area, Area, double()); +PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly @@ -68,9 +68,9 @@ Here is another demo showing how to define overloads in a dispatch. Note that `. // Abstraction (poly is short for polymorphism) namespace poly { -DEFINE_MEMBER_DISPATCH(Log, Log, +PRO_DEF_MEMBER_DISPATCH(Log, Log, void(const char*), void(const char*, const std::exception&)); -DEFINE_FACADE(Logger, Log); +PRO_DEF_FACADE(Logger, Log); } // namespace poly diff --git a/proxy.h b/proxy.h index 38501ad..31cee9d 100644 --- a/proxy.h +++ b/proxy.h @@ -584,7 +584,7 @@ proxy make_proxy(T&& value) { } // The following types and macros aim to simplify definition of dispatch and -// facade types before C++26 +// facade types prior to C++26 namespace helper { template , proxy_pointer_constraints C = @@ -598,7 +598,8 @@ struct facade_prototype { } // namespace helper } // namespace pro -#define DEFINE_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ + +#define PRO_DEF_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ struct __NAME { \ using overload_types = std::tuple<__VA_ARGS__>;\ template \ @@ -609,7 +610,7 @@ struct facade_prototype { .__FUNC(std::forward<__Args>(__args)...); \ } \ } -#define DEFINE_FREE_DISPATCH(__NAME, __FUNC, ...) \ +#define PRO_DEF_FREE_DISPATCH(__NAME, __FUNC, ...) \ struct __NAME { \ using overload_types = std::tuple<__VA_ARGS__>;\ template \ @@ -620,8 +621,8 @@ struct facade_prototype { std::forward<__Args>(__args)...); \ } \ } -#define MAKE_DISPATCH_PACK(...) std::tuple<__VA_ARGS__> -#define DEFINE_FACADE(__NAME, ...) \ +#define PRO_MAKE_DISPATCH_PACK(...) std::tuple<__VA_ARGS__> +#define PRO_DEF_FACADE(__NAME, ...) \ struct __NAME : ::pro::helper::facade_prototype<__VA_ARGS__> {} #endif // _MSFT_PROXY_ diff --git a/samples/resource_dictionary/main.cpp b/samples/resource_dictionary/main.cpp index 1f0a53a..562b347 100644 --- a/samples/resource_dictionary/main.cpp +++ b/samples/resource_dictionary/main.cpp @@ -7,8 +7,8 @@ namespace poly { -DEFINE_MEMBER_DISPATCH(At, at, std::string(int)); -DEFINE_FACADE(Dictionary, At); +PRO_DEF_MEMBER_DISPATCH(At, at, std::string(int)); +PRO_DEF_FACADE(Dictionary, At); } // namespace poly diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 33eac93..f863799 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -21,14 +21,14 @@ struct SboObserver { bool SboEnabled; }; -DEFINE_FACADE(TestSmallStringable, utils::poly::ToString, pro::proxy_pointer_constraints{ +PRO_DEF_FACADE(TestSmallStringable, utils::poly::ToString, pro::proxy_pointer_constraints{ .maximum_size = sizeof(void*), .maximum_alignment = alignof(void*), .minimum_copyability = pro::constraint_level::nontrivial, .minimum_relocatability = pro::constraint_level::nothrow, .minimum_destructibility = pro::constraint_level::nothrow, }, SboObserver); -DEFINE_FACADE(TestLargeStringable, utils::poly::ToString, pro::copyable_pointer_constraints, SboObserver); +PRO_DEF_FACADE(TestLargeStringable, utils::poly::ToString, pro::copyable_pointer_constraints, SboObserver); } // namespace poly diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index 782d9ba..cddee09 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -15,9 +15,9 @@ namespace { namespace poly { -DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); -DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(Drawable, MAKE_DISPATCH_PACK(Draw, Area)); +PRO_DEF_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); +PRO_DEF_MEMBER_DISPATCH(Area, Area, double()); +PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index b98d688..1caab52 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -16,20 +16,20 @@ namespace { namespace poly { template -DEFINE_MEMBER_DISPATCH(Call, operator(), Os...); +PRO_DEF_MEMBER_DISPATCH(Call, operator(), Os...); template -DEFINE_FACADE(Callable, Call, pro::copyable_pointer_constraints); +PRO_DEF_FACADE(Callable, Call, pro::copyable_pointer_constraints); -DEFINE_FREE_DISPATCH(GetSize, std::ranges::size, std::size_t()); +PRO_DEF_FREE_DISPATCH(GetSize, std::ranges::size, std::size_t()); template -DEFINE_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); +PRO_DEF_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); template -DEFINE_FACADE(Iterable, MAKE_DISPATCH_PACK(ForEach, GetSize)); +PRO_DEF_FACADE(Iterable, PRO_MAKE_DISPATCH_PACK(ForEach, GetSize)); template struct Append; template -DEFINE_FACADE(Container, MAKE_DISPATCH_PACK(ForEach, GetSize, Append)); +PRO_DEF_FACADE(Container, PRO_MAKE_DISPATCH_PACK(ForEach, GetSize, Append)); template struct Append { using overload_types = std::tuple>(T)>; @@ -117,7 +117,7 @@ TEST(ProxyInvocationTests, TestMultipleDispatches_Unique) { TEST(ProxyInvocationTests, TestMultipleDispatches_Duplicated) { using SomeCombination = std::tuple, std::tuple>>; - DEFINE_FACADE(DuplicatedIterable, MAKE_DISPATCH_PACK(poly::ForEach, SomeCombination, poly::ForEach, poly::GetSize, poly::GetSize)); + PRO_DEF_FACADE(DuplicatedIterable, PRO_MAKE_DISPATCH_PACK(poly::ForEach, SomeCombination, poly::ForEach, poly::GetSize, poly::GetSize)); static_assert(sizeof(pro::details::facade_traits::meta_type) == sizeof(pro::details::facade_traits>::meta_type)); std::list l = { 1, 2, 3 }; diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 7dad605..cee2306 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -7,7 +7,7 @@ namespace { -DEFINE_FACADE(TestFacade, utils::poly::ToString, pro::copyable_pointer_constraints); +PRO_DEF_FACADE(TestFacade, utils::poly::ToString, pro::copyable_pointer_constraints); } // namespace diff --git a/tests/proxy_reflection_tests.cpp b/tests/proxy_reflection_tests.cpp index 4d3fa62..5a65385 100644 --- a/tests/proxy_reflection_tests.cpp +++ b/tests/proxy_reflection_tests.cpp @@ -42,13 +42,13 @@ struct TraitsReflection { bool is_trivial_; }; -DEFINE_FACADE(DefaultFacade); +PRO_DEF_FACADE(DefaultFacade); static_assert(!ReflectionApplicable); -DEFINE_FACADE(TestRttiFacade, MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, RttiReflection); +PRO_DEF_FACADE(TestRttiFacade, PRO_MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, RttiReflection); static_assert(ReflectionApplicable); -DEFINE_FACADE(TestTraitsFacade, MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, TraitsReflection); +PRO_DEF_FACADE(TestTraitsFacade, PRO_MAKE_DISPATCH_PACK(), pro::relocatable_pointer_constraints, TraitsReflection); static_assert(ReflectionApplicable); } // namespace diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index f807d37..41daddd 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -23,7 +23,7 @@ using MockCopyablePtr = MockPtr; using MockTrivialPtr = MockPtr; -DEFINE_FACADE(DefaultFacade); +PRO_DEF_FACADE(DefaultFacade); static_assert(DefaultFacade::pointer_constraints.minimum_copyability == pro::constraint_level::none); static_assert(DefaultFacade::pointer_constraints.minimum_relocatability == pro::constraint_level::nothrow); static_assert(DefaultFacade::pointer_constraints.minimum_destructibility == pro::constraint_level::nothrow); @@ -40,7 +40,7 @@ static_assert(std::is_nothrow_constructible_v, std::in static_assert(std::is_nothrow_constructible_v, std::in_place_type_t, int>); static_assert(std::is_nothrow_constructible_v, std::in_place_type_t, int>); -DEFINE_FACADE(RelocatableFacade); +PRO_DEF_FACADE(RelocatableFacade); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_nothrow_move_constructible_v>); @@ -64,7 +64,7 @@ static_assert(std::is_nothrow_assignable_v, MockCo static_assert(std::is_nothrow_constructible_v, MockTrivialPtr>); static_assert(std::is_nothrow_assignable_v, MockTrivialPtr>); -DEFINE_FACADE(CopyableFacade, MAKE_DISPATCH_PACK(), pro::copyable_pointer_constraints); +PRO_DEF_FACADE(CopyableFacade, PRO_MAKE_DISPATCH_PACK(), pro::copyable_pointer_constraints); static_assert(std::is_copy_constructible_v>); static_assert(!std::is_nothrow_copy_constructible_v>); static_assert(std::is_copy_assignable_v>); @@ -88,7 +88,7 @@ static_assert(std::is_nothrow_assignable_v, MockCopya static_assert(std::is_nothrow_constructible_v, MockTrivialPtr>); static_assert(std::is_nothrow_assignable_v, MockTrivialPtr>); -DEFINE_FACADE(CopyableSmallFacade, MAKE_DISPATCH_PACK(), pro::proxy_pointer_constraints{ +PRO_DEF_FACADE(CopyableSmallFacade, PRO_MAKE_DISPATCH_PACK(), pro::proxy_pointer_constraints{ .maximum_size = sizeof(void*), .maximum_alignment = alignof(void*), .minimum_copyability = pro::constraint_level::nontrivial, @@ -108,7 +108,7 @@ static_assert(std::is_nothrow_assignable_v, Mock static_assert(std::is_constructible_v, MockTrivialPtr>); static_assert(std::is_assignable_v, MockTrivialPtr>); -DEFINE_FACADE(TrivialFacade, MAKE_DISPATCH_PACK(), pro::trivial_pointer_constraints); +PRO_DEF_FACADE(TrivialFacade, PRO_MAKE_DISPATCH_PACK(), pro::trivial_pointer_constraints); static_assert(std::is_trivially_copy_constructible_v>); static_assert(std::is_trivially_copy_assignable_v>); static_assert(std::is_nothrow_move_constructible_v>); diff --git a/tests/utils.h b/tests/utils.h index aa7e45b..ed62de5 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -83,7 +83,7 @@ class LifetimeTracker { namespace poly { using std::to_string; -DEFINE_FREE_DISPATCH(ToString, to_string, std::string()); +PRO_DEF_FREE_DISPATCH(ToString, to_string, std::string()); } // namespace poly From 87d9a3cd3599bc7a8de4c2f1a09de9c066d61c38 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Tue, 19 Dec 2023 23:26:26 +0800 Subject: [PATCH 4/6] Wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12ec6f9..c0e24ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Proxy: Redefine Polymorphism in C++ +# Proxy: Polymorphism in C++ Redefined [![Proxy-CI](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml/badge.svg)](https://github.com/microsoft/proxy/actions/workflows/pipeline-ci.yml) From db3b3bd81ad4372c5571a389cf5f6a7e081289e1 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 21 Dec 2023 18:36:11 +0800 Subject: [PATCH 5/6] Fix concepts --- proxy.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/proxy.h b/proxy.h index 31cee9d..80f4492 100644 --- a/proxy.h +++ b/proxy.h @@ -295,19 +295,21 @@ using dependent_t = typename dependent_traits::type; } // namespace details template -concept facade = requires { +concept basic_facade = requires { typename F::dispatch_types; typename std::integral_constant< proxy_pointer_constraints, F::pointer_constraints>; typename F::reflection_type; }; +template +concept facade = basic_facade && details::facade_traits::applicable; + template concept proxiable = facade && details::is_address_deducible && - details::facade_traits::applicable && details::facade_traits::template applicable_pointer

; -template +template class proxy { using BasicTraits = details::basic_facade_traits; using Traits = details::facade_traits; @@ -495,8 +497,8 @@ class proxy { } template decltype(auto) invoke(Args&&... args) const - requires(BasicTraits::template has_dispatch && - details::dependent_t::applicable && + requires(facade> && + BasicTraits::template has_dispatch && details::dispatch_traits::template has_overload) { constexpr std::size_t OverloadIndex = details::dispatch_traits::template overload_index; @@ -508,8 +510,8 @@ class proxy { template decltype(auto) operator()(Args&&... args) const - requires(!std::is_void_v && - details::dependent_t::applicable && + requires(facade> && + !std::is_void_v && details::dependent_t , Args...>::template has_overload) { return invoke(std::forward(args)...); } From bc8def71fee66b64221b3822f528418efbd50701 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Fri, 22 Dec 2023 12:09:35 +0800 Subject: [PATCH 6/6] Resolve comments --- README.md | 47 +++++++++++++++++++++++++--- proxy.h | 6 ++-- samples/resource_dictionary/main.cpp | 4 +-- tests/proxy_integration_tests.cpp | 4 +-- tests/proxy_invocation_tests.cpp | 2 +- 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c0e24ab..81cb9b2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Proxy: Polymorphism in C++ Redefined +# 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) @@ -24,8 +24,8 @@ The majority of the library is defined in namespace `pro`. Some macros are provi // Abstraction (poly is short for polymorphism) namespace poly { -PRO_DEF_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); -PRO_DEF_MEMBER_DISPATCH(Area, Area, double()); +PRO_DEF_MEMBER_DISPATCH(Draw, void(std::ostream&)); +PRO_DEF_MEMBER_DISPATCH(Area, double()); PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly @@ -68,8 +68,7 @@ Here is another demo showing how to define overloads in a dispatch. Note that `. // Abstraction (poly is short for polymorphism) namespace poly { -PRO_DEF_MEMBER_DISPATCH(Log, Log, - void(const char*), void(const char*, const std::exception&)); +PRO_DEF_MEMBER_DISPATCH(Log, void(const char*), void(const char*, const std::exception&)); PRO_DEF_FACADE(Logger, Log); } // namespace poly @@ -102,6 +101,44 @@ int main() { } ``` +By design, the body of a dispatch could be any code. While member function is one useful pattern supported by macro `PRO_DEF_MEMBER_DISPATCH`, free function is also supported with another macro `PRO_DEF_FREE_DISPATCH`. The following example uses `PRO_DEF_FREE_DISPATCH` and `std::invoke` to implement similar function wrapper as `std::function` and `std::move_only_function` and supports multiple overloads. + +```cpp +// Abstraction (poly is short for polymorphism) +namespace poly { + +template +PRO_DEF_FREE_DISPATCH(Call, std::invoke, Overloads...); +template +PRO_DEF_FACADE(MovableCallable, Call); +template +PRO_DEF_FACADE(CopyableCallable, Call, pro::copyable_pointer_constraints); + +} // namespace poly + +// MyFunction has similar functionality as std::function but supports multiple overloads +// MyMoveOnlyFunction has similar functionality as std::move_only_function but supports multiple overloads +template +using MyFunction = pro::proxy>; +template +using MyMoveOnlyFunction = pro::proxy>; + +int main() { + auto f = [](auto&&... v) { + printf("f() called. Args: "); + ((std::cout << v << ":" << typeid(decltype(v)).name() << ", "), ...); + puts(""); + }; + MyFunction p0{&f}; + p0(123); // Prints "f() called. Args: 123:i," (assuming GCC) + MyMoveOnlyFunction p1{&f}; + p1(); // Prints "f() called. Args:" + p1(456); // Prints "f() called. Args: 456:i," + p1(1.2); // Prints "f() called. Args: 1.2:d," + return 0; +} +``` + Please find more details and discussions in the spec. The complete version of the "drawable" demo could be found in [tests/proxy_integration_tests.cpp](tests/proxy_integration_tests.cpp) (also available on [Compiler Explorer](https://godbolt.org/z/5a3jeE1M8)). ## Minimum requirements for compilers diff --git a/proxy.h b/proxy.h index 80f4492..4004ca8 100644 --- a/proxy.h +++ b/proxy.h @@ -601,15 +601,15 @@ struct facade_prototype { } // namespace pro -#define PRO_DEF_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ +#define PRO_DEF_MEMBER_DISPATCH(__NAME, ...) \ struct __NAME { \ using overload_types = std::tuple<__VA_ARGS__>;\ template \ decltype(auto) operator()(__T&& __self, __Args&&... __args) \ requires(requires{ std::forward<__T>(__self) \ - .__FUNC(std::forward<__Args>(__args)...); }) { \ + .__NAME(std::forward<__Args>(__args)...); }) { \ return std::forward<__T>(__self) \ - .__FUNC(std::forward<__Args>(__args)...); \ + .__NAME(std::forward<__Args>(__args)...); \ } \ } #define PRO_DEF_FREE_DISPATCH(__NAME, __FUNC, ...) \ diff --git a/samples/resource_dictionary/main.cpp b/samples/resource_dictionary/main.cpp index 562b347..7b50e9d 100644 --- a/samples/resource_dictionary/main.cpp +++ b/samples/resource_dictionary/main.cpp @@ -7,8 +7,8 @@ namespace poly { -PRO_DEF_MEMBER_DISPATCH(At, at, std::string(int)); -PRO_DEF_FACADE(Dictionary, At); +PRO_DEF_MEMBER_DISPATCH(at, std::string(int)); +PRO_DEF_FACADE(Dictionary, at); } // namespace poly diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index cddee09..324c6f5 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -15,8 +15,8 @@ namespace { namespace poly { -PRO_DEF_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); -PRO_DEF_MEMBER_DISPATCH(Area, Area, double()); +PRO_DEF_MEMBER_DISPATCH(Draw, void(std::ostream&)); +PRO_DEF_MEMBER_DISPATCH(Area, double()); PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area)); } // namespace poly diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 1caab52..106d9c5 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -16,7 +16,7 @@ namespace { namespace poly { template -PRO_DEF_MEMBER_DISPATCH(Call, operator(), Os...); +PRO_DEF_FREE_DISPATCH(Call, std::invoke, Os...); template PRO_DEF_FACADE(Callable, Call, pro::copyable_pointer_constraints);