From af83bf8f3013da914b1e4ee348a4bb1dc74d97f6 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Wed, 29 Nov 2023 18:45:42 +0800 Subject: [PATCH 1/3] Update macro --- README.md | 13 ++--- proxy.h | 71 +++++++++++++++++++--------- samples/resource_dictionary/main.cpp | 6 +-- tests/proxy_integration_tests.cpp | 13 ++--- tests/proxy_invocation_tests.cpp | 24 +++------- tests/utils.h | 15 +++--- 6 files changed, 73 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 1039adb..4e055ca 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,14 @@ The "proxy" is a single-header, cross-platform C++ library that Microsoft uses t 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`. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach: +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: ```cpp // Abstraction -struct Draw : pro::dispatch { - void operator()(const auto& self, std::ostream& out) { self.Draw(out); } -}; -struct Area : pro::dispatch { - double operator()(const auto& self) { return self.Area(); } -}; -struct DrawableFacade : pro::facade {}; + +DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); +DEFINE_MEMBER_DISPATCH(Area, Area, double()); +DEFINE_FACADE(DrawableFacade, Draw, Area); // Implementation class Rectangle { diff --git a/proxy.h b/proxy.h index b197347..003b220 100644 --- a/proxy.h +++ b/proxy.h @@ -15,28 +15,6 @@ namespace pro { enum class constraint_level { none, nontrivial, nothrow, trivial }; -template -struct dispatch { using overload_types = std::tuple; }; -template -struct dispatch_adaptor : dispatch { - template - requires(std::is_invocable_v) - constexpr decltype(auto) operator()(T&& value, Args&&... args) const - { return CPO(std::forward(value), std::forward(args)...); } -}; - -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; -}; - namespace details { struct applicable_traits { static constexpr bool applicable = true; }; @@ -123,7 +101,11 @@ struct overload_traits : applicable_traits { { { D{}(std::forward(operand), std::forward(args)...) }; }; template static R dispatcher(const char* p, Args... args) { - return D{}(**reinterpret_cast(p), std::forward(args)...); + if constexpr (std::is_void_v) { + D{}(**reinterpret_cast(p), std::forward(args)...); + } else { + return D{}(**reinterpret_cast(p), std::forward(args)...); + } } }; @@ -566,6 +548,49 @@ 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; +}; + } // namespace pro +// The following macros facilitate definition of dispatch and facade types +#define DEFINE_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ + struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ + template \ + decltype(auto) operator()(T&& value, Args&&... args) \ + requires(requires{\ + std::forward(value).__FUNC(std::forward(args)...); }) { \ + return std::forward(value).__FUNC(std::forward(args)...); \ + } \ + } +#define DEFINE_FREE_DISPATCH(__NAME, __FUNC, ...) \ + struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ + template \ + decltype(auto) operator()(T&& value, Args&&... args) \ + requires(requires{\ + __FUNC(std::forward(value), std::forward(args)...); }) { \ + return __FUNC(std::forward(value), std::forward(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; \ + } + #endif // _MSFT_PROXY_ diff --git a/samples/resource_dictionary/main.cpp b/samples/resource_dictionary/main.cpp index 6846f6a..d943067 100644 --- a/samples/resource_dictionary/main.cpp +++ b/samples/resource_dictionary/main.cpp @@ -5,10 +5,8 @@ #include -struct at : pro::dispatch { - auto operator()(const auto& self, int key) { return self.at(key); } -}; -struct resource_dictionary : pro::facade {}; +DEFINE_MEMBER_DISPATCH(at, at, std::string(int)); +DEFINE_FACADE(resource_dictionary, at); void demo_print(pro::proxy dictionary) { std::cout << dictionary(1) << std::endl; diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index 0925ff5..cfe0d78 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -13,16 +13,9 @@ namespace { -struct Draw : pro::dispatch { - template - void operator()(const T& self, std::ostream& out) { self.Draw(out); } -}; -struct Area : pro::dispatch { - template - double operator()(const T& self) { return self.Area(); } -}; - -struct DrawableFacade : pro::facade {}; +DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); +DEFINE_MEMBER_DISPATCH(Area, Area, double()); +DEFINE_FACADE(DrawableFacade, Draw, Area); class Rectangle { public: diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index b3d29cd..e158d68 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include +#include #include #include #include @@ -13,31 +14,20 @@ namespace { template -struct Call : pro::dispatch { - template requires(std::is_invocable_v) - decltype(auto) operator()(T& self, Args&&... args) - { return self(std::forward(args)...); } -}; +DEFINE_MEMBER_DISPATCH(Call, operator(), Os...); template -struct CallableFacade : pro::facade> {}; +DEFINE_COPYABLE_FACADE(CallableFacade, Call); -struct GetSize : pro::dispatch_adaptor {}; +DEFINE_FREE_DISPATCH(GetSize, std::ranges::size, std::size_t()); template -struct ForEach : pro::dispatch>)> { - template - void operator()(U& self, pro::proxy>&& func) { - for (auto& value : self) { - func(value); - } - } -}; +DEFINE_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); template -struct IterableFacade : pro::facade, GetSize> {}; +DEFINE_FACADE(IterableFacade, ForEach, GetSize); template struct Append; template -struct ContainerFacade : pro::facade, GetSize, Append> {}; +DEFINE_FACADE(ContainerFacade, ForEach, GetSize, Append); template struct Append : pro::dispatch>(T)> { template diff --git a/tests/utils.h b/tests/utils.h index a1290cc..23ce976 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -80,13 +80,14 @@ class LifetimeTracker { std::vector ops_; }; -struct ToString : pro::dispatch { - template - std::string operator()(const T& self) { - using std::to_string; - return to_string(self); - } -}; +namespace details { + +using std::to_string; +DEFINE_FREE_DISPATCH(ToString, to_string, std::string()); + +} // namespace details + +using details::ToString; } // namespace utils From d28fc096243b22f5a453140232f1924bd1d88496 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Wed, 29 Nov 2023 19:00:42 +0800 Subject: [PATCH 2/3] Fix build error --- proxy.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/proxy.h b/proxy.h index 003b220..9238a66 100644 --- a/proxy.h +++ b/proxy.h @@ -568,20 +568,22 @@ struct facade { // The following macros facilitate definition of dispatch and facade types #define DEFINE_MEMBER_DISPATCH(__NAME, __FUNC, ...) \ struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ - template \ - decltype(auto) operator()(T&& value, Args&&... args) \ - requires(requires{\ - std::forward(value).__FUNC(std::forward(args)...); }) { \ - return std::forward(value).__FUNC(std::forward(args)...); \ + template \ + decltype(auto) operator()(__T&& __self, __Args&&... __args) \ + requires(requires{ std::forward<__T>(__self) \ + .__FUNC(std::forward<__Args>(__args)...); }) { \ + return std::forward<__T>(__self) \ + .__FUNC(std::forward<__Args>(__args)...); \ } \ } #define DEFINE_FREE_DISPATCH(__NAME, __FUNC, ...) \ struct __NAME : ::pro::dispatch<__VA_ARGS__> { \ - template \ - decltype(auto) operator()(T&& value, Args&&... args) \ - requires(requires{\ - __FUNC(std::forward(value), std::forward(args)...); }) { \ - return __FUNC(std::forward(value), std::forward(args)...); \ + template \ + decltype(auto) operator()(__T&& __self, __Args&&... __args) \ + requires(requires{ __FUNC(std::forward<__T>(__self), \ + std::forward<__Args>(__args)...); }) { \ + return __FUNC(std::forward<__T>(__self), \ + std::forward<__Args>(__args)...); \ } \ } From 9e3f4df6a2fe2b3eb6cf97006ab40ea6fe4693bc Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Wed, 29 Nov 2023 22:32:31 +0800 Subject: [PATCH 3/3] Resolve comments --- README.md | 17 ++++--- samples/resource_dictionary/main.cpp | 10 ++-- tests/proxy_creation_tests.cpp | 68 +++++++++++++------------ tests/proxy_integration_tests.cpp | 18 ++++--- tests/proxy_invocation_tests.cpp | 75 +++++++++++++++------------- tests/proxy_lifetime_tests.cpp | 2 +- tests/utils.h | 6 +-- 7 files changed, 107 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 4e055ca..8757fdd 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,14 @@ The "proxy" is a header-only C++20 library. Once you set the language level of y 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: ```cpp -// Abstraction +// Abstraction (poly is short for polymorphism) +namespace poly { DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(DrawableFacade, Draw, Area); +DEFINE_FACADE(Drawable, Draw, Area); + +} // namespace poly // Implementation class Rectangle { @@ -40,20 +43,20 @@ class Rectangle { }; // Client - Consumer -std::string PrintDrawableToString(pro::proxy p) { +std::string PrintDrawableToString(pro::proxy p) { std::stringstream result; result << "shape = "; - p.invoke(result); - result << ", area = " << p.invoke(); + p.invoke(result); + result << ", area = " << p.invoke(); return std::move(result).str(); } // Client - Producer -pro::proxy CreateRectangleAsDrawable(int width, int height) { +pro::proxy CreateRectangleAsDrawable(int width, int height) { Rectangle rect; rect.SetWidth(width); rect.SetHeight(height); - return pro::make_proxy(rect); + return pro::make_proxy(rect); } ``` diff --git a/samples/resource_dictionary/main.cpp b/samples/resource_dictionary/main.cpp index d943067..1f0a53a 100644 --- a/samples/resource_dictionary/main.cpp +++ b/samples/resource_dictionary/main.cpp @@ -5,10 +5,14 @@ #include -DEFINE_MEMBER_DISPATCH(at, at, std::string(int)); -DEFINE_FACADE(resource_dictionary, at); +namespace poly { -void demo_print(pro::proxy dictionary) { +DEFINE_MEMBER_DISPATCH(At, at, std::string(int)); +DEFINE_FACADE(Dictionary, At); + +} // namespace poly + +void demo_print(pro::proxy dictionary) { std::cout << dictionary(1) << std::endl; } diff --git a/tests/proxy_creation_tests.cpp b/tests/proxy_creation_tests.cpp index 936a7a7..9e140e9 100644 --- a/tests/proxy_creation_tests.cpp +++ b/tests/proxy_creation_tests.cpp @@ -7,28 +7,32 @@ namespace { -struct TraitsReflection { +namespace poly { + +struct SboObserver { public: template - constexpr explicit TraitsReflection(std::in_place_type_t>) - : sbo_enabled_(true) {} + constexpr explicit SboObserver(std::in_place_type_t>) + : SboEnabled(true) {} template - constexpr explicit TraitsReflection(std::in_place_type_t>) - : sbo_enabled_(false) {} + constexpr explicit SboObserver(std::in_place_type_t

) + : SboEnabled(false) {} - bool sbo_enabled_; + bool SboEnabled; }; -struct TestSmallFacade : pro::facade { - using reflection_type = TraitsReflection; +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 TestLargeFacade : pro::facade { - using reflection_type = TraitsReflection; +struct TestLargeStringable : pro::facade { + using reflection_type = SboObserver; static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; }; +} // namespace poly + } // namespace TEST(ProxyCreationTests, TestMakeProxy_WithSBO_FromValue) { @@ -37,10 +41,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_FromValue) { utils::LifetimeTracker::Session session{ &tracker }; expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); { - auto p = pro::make_proxy(session); + auto p = pro::make_proxy(session); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 2"); - ASSERT_TRUE(p.reflect().sbo_enabled_); + ASSERT_TRUE(p.reflect().SboEnabled); expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -52,10 +56,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlace) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p = pro::make_proxy(&tracker); + auto p = pro::make_proxy(&tracker); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 1"); - ASSERT_TRUE(p.reflect().sbo_enabled_); + ASSERT_TRUE(p.reflect().SboEnabled); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -67,10 +71,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_InPlaceInitializerList) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p = pro::make_proxy({ 1, 2, 3 }, &tracker); + auto p = pro::make_proxy({ 1, 2, 3 }, &tracker); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 1"); - ASSERT_TRUE(p.reflect().sbo_enabled_); + ASSERT_TRUE(p.reflect().SboEnabled); expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -82,15 +86,15 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Copy) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p1 = pro::make_proxy(&tracker); + auto p1 = pro::make_proxy(&tracker); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); auto p2 = p1; ASSERT_TRUE(p1.has_value()); ASSERT_EQ(p1.invoke(), "Session 1"); - ASSERT_TRUE(p1.reflect().sbo_enabled_); + ASSERT_TRUE(p1.reflect().SboEnabled); ASSERT_TRUE(p2.has_value()); ASSERT_EQ(p2.invoke(), "Session 2"); - ASSERT_TRUE(p2.reflect().sbo_enabled_); + ASSERT_TRUE(p2.reflect().SboEnabled); expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -103,13 +107,13 @@ TEST(ProxyCreationTests, TestMakeProxy_WithSBO_Lifetime_Move) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p1 = pro::make_proxy(&tracker); + auto p1 = pro::make_proxy(&tracker); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); auto p2 = std::move(p1); ASSERT_FALSE(p1.has_value()); ASSERT_TRUE(p2.has_value()); ASSERT_EQ(p2.invoke(), "Session 2"); - ASSERT_TRUE(p2.reflect().sbo_enabled_); + ASSERT_TRUE(p2.reflect().SboEnabled); expected_ops.emplace_back(2, utils::LifetimeOperationType::kMoveConstruction); expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); @@ -124,10 +128,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_FromValue) { utils::LifetimeTracker::Session session{ &tracker }; expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); { - auto p = pro::make_proxy(session); + auto p = pro::make_proxy(session); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 2"); - ASSERT_FALSE(p.reflect().sbo_enabled_); + ASSERT_FALSE(p.reflect().SboEnabled); expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -139,10 +143,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlace) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p = pro::make_proxy(&tracker); + auto p = pro::make_proxy(&tracker); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 1"); - ASSERT_FALSE(p.reflect().sbo_enabled_); + ASSERT_FALSE(p.reflect().SboEnabled); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -154,10 +158,10 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_InPlaceInitializerList) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p = pro::make_proxy({ 1, 2, 3 }, &tracker); + auto p = pro::make_proxy({ 1, 2, 3 }, &tracker); ASSERT_TRUE(p.has_value()); ASSERT_EQ(p.invoke(), "Session 1"); - ASSERT_FALSE(p.reflect().sbo_enabled_); + ASSERT_FALSE(p.reflect().SboEnabled); expected_ops.emplace_back(1, utils::LifetimeOperationType::kInitializerListConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -169,15 +173,15 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Copy) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p1 = pro::make_proxy(&tracker); + auto p1 = pro::make_proxy(&tracker); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); auto p2 = p1; ASSERT_TRUE(p1.has_value()); ASSERT_EQ(p1.invoke(), "Session 1"); - ASSERT_FALSE(p1.reflect().sbo_enabled_); + ASSERT_FALSE(p1.reflect().SboEnabled); ASSERT_TRUE(p2.has_value()); ASSERT_EQ(p2.invoke(), "Session 2"); - ASSERT_FALSE(p2.reflect().sbo_enabled_); + ASSERT_FALSE(p2.reflect().SboEnabled); expected_ops.emplace_back(2, utils::LifetimeOperationType::kCopyConstruction); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } @@ -190,13 +194,13 @@ TEST(ProxyCreationTests, TestMakeProxy_WithoutSBO_Lifetime_Move) { utils::LifetimeTracker tracker; std::vector expected_ops; { - auto p1 = pro::make_proxy(&tracker); + auto p1 = pro::make_proxy(&tracker); expected_ops.emplace_back(1, utils::LifetimeOperationType::kValueConstruction); auto p2 = std::move(p1); ASSERT_FALSE(p1.has_value()); ASSERT_TRUE(p2.has_value()); ASSERT_EQ(p2.invoke(), "Session 1"); - ASSERT_FALSE(p2.reflect().sbo_enabled_); + ASSERT_FALSE(p2.reflect().SboEnabled); ASSERT_TRUE(tracker.GetOperations() == expected_ops); } expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction); diff --git a/tests/proxy_integration_tests.cpp b/tests/proxy_integration_tests.cpp index cfe0d78..5ab07f9 100644 --- a/tests/proxy_integration_tests.cpp +++ b/tests/proxy_integration_tests.cpp @@ -13,9 +13,13 @@ namespace { +namespace poly { + DEFINE_MEMBER_DISPATCH(Draw, Draw, void(std::ostream&)); DEFINE_MEMBER_DISPATCH(Area, Area, double()); -DEFINE_FACADE(DrawableFacade, Draw, Area); +DEFINE_FACADE(Drawable, Draw, Area); + +} // namespace poly class Rectangle { public: @@ -46,11 +50,11 @@ class Point { constexpr double Area() const { return 0; } }; -std::string PrintDrawableToString(pro::proxy p) { +std::string PrintDrawableToString(pro::proxy p) { std::stringstream result; result << std::fixed << std::setprecision(5) << "shape = "; - p.invoke(result); - result << ", area = " << p.invoke(); + p.invoke(result); + result << ", area = " << p.invoke(); return std::move(result).str(); } @@ -77,7 +81,7 @@ std::vector ParseCommand(const std::string& s) { return result; } -pro::proxy MakeDrawableFromCommand(const std::string& s) { +pro::proxy MakeDrawableFromCommand(const std::string& s) { std::vector parsed = ParseCommand(s); if (!parsed.empty()) { if (parsed[0u] == "Rectangle") { @@ -96,7 +100,7 @@ pro::proxy MakeDrawableFromCommand(const std::string& s) { if (parsed.size() == 2u) { Circle circle; circle.SetRadius(std::stod(parsed[1u])); - return pro::make_proxy(circle); + return pro::make_proxy(circle); } } else if (parsed[0u] == "Point") { if (parsed.size() == 1u) { @@ -111,7 +115,7 @@ pro::proxy MakeDrawableFromCommand(const std::string& s) { } // namespace TEST(ProxyIntegrationTests, TestDrawable) { - pro::proxy p = MakeDrawableFromCommand("Rectangle 2 3"); + pro::proxy p = MakeDrawableFromCommand("Rectangle 2 3"); std::string s = PrintDrawableToString(std::move(p)); ASSERT_EQ(s, "shape = {Rectangle: width = 2.00000, height = 3.00000}, area = 6.00000"); diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index e158d68..f37999e 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -13,30 +13,35 @@ namespace { +namespace poly { + template DEFINE_MEMBER_DISPATCH(Call, operator(), Os...); template -DEFINE_COPYABLE_FACADE(CallableFacade, Call); +DEFINE_COPYABLE_FACADE(Callable, Call); DEFINE_FREE_DISPATCH(GetSize, std::ranges::size, std::size_t()); template -DEFINE_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); +DEFINE_FREE_DISPATCH(ForEach, std::ranges::for_each, void(pro::proxy>)); template -DEFINE_FACADE(IterableFacade, ForEach, GetSize); +DEFINE_FACADE(Iterable, ForEach, GetSize); template struct Append; template -DEFINE_FACADE(ContainerFacade, ForEach, GetSize, Append); +DEFINE_FACADE(Container, ForEach, GetSize, Append); template -struct Append : pro::dispatch>(T)> { +struct Append : pro::dispatch>(T)> { template - pro::proxy> operator()(U& self, T&& value) { + pro::proxy> operator()(U& self, T&& value) { self.push_back(std::move(value)); return &self; } }; +} // namespace poly + + template concept InvocableWithDispatch = requires(pro::proxy p, Args... args) { { p.template invoke(std::forward(args)...) }; }; @@ -44,17 +49,17 @@ template concept InvocableWithoutDispatch = std::is_invocable_v, Args...>; // Static assertions for a facade of a single dispatch -static_assert(InvocableWithDispatch, Call, double>); -static_assert(!InvocableWithDispatch, Call, std::nullptr_t>); // Wrong arguments -static_assert(!InvocableWithoutDispatch, std::nullptr_t>); // Wrong arguments -static_assert(!InvocableWithDispatch, int(double), double>); // Wrong dispatch -static_assert(InvocableWithoutDispatch, float>); // Invoking without specifying a dispatch +static_assert(InvocableWithDispatch, poly::Call, double>); +static_assert(!InvocableWithDispatch, poly::Call, std::nullptr_t>); // Wrong arguments +static_assert(!InvocableWithoutDispatch, std::nullptr_t>); // Wrong arguments +static_assert(!InvocableWithDispatch, int(double), double>); // Wrong dispatch +static_assert(InvocableWithoutDispatch, float>); // Invoking without specifying a dispatch // Static assertions for a facade of multiple dispatches -static_assert(InvocableWithDispatch, GetSize>); -static_assert(!InvocableWithDispatch, ForEach, pro::proxy>>); // Wrong arguments -static_assert(!InvocableWithDispatch, Append>); // Wrong dispatch -static_assert(!InvocableWithoutDispatch>); // Invoking without specifying a dispatch +static_assert(InvocableWithDispatch, poly::GetSize>); +static_assert(!InvocableWithDispatch, poly::ForEach, pro::proxy>>); // Wrong arguments +static_assert(!InvocableWithDispatch, poly::Append>); // Wrong dispatch +static_assert(!InvocableWithoutDispatch>); // Invoking without specifying a dispatch template std::vector GetTypeIndices() @@ -74,7 +79,7 @@ TEST(ProxyInvocationTests, TestArgumentForwarding) { arg2_received = std::move(v); return expected_result; }; - pro::proxy)>> p = &f; + pro::proxy)>> p = &f; int result = p.invoke(arg1, std::move(arg2)); ASSERT_TRUE(p.has_value()); ASSERT_EQ(arg1_received, arg1); @@ -87,7 +92,7 @@ TEST(ProxyInvocationTests, TestThrow) { const char* expected_error_message = "My exception"; auto f = [&] { throw std::runtime_error{ expected_error_message }; }; bool exception_thrown = false; - pro::proxy> p = &f; + pro::proxy> p = &f; try { p.invoke(); } catch (const std::runtime_error& e) { @@ -100,46 +105,46 @@ TEST(ProxyInvocationTests, TestThrow) { TEST(ProxyInvocationTests, TestMultipleDispatches_Unique) { std::list l = { 1, 2, 3 }; - pro::proxy> p = &l; - ASSERT_EQ(p.invoke(), 3); + pro::proxy> p = &l; + ASSERT_EQ(p.invoke(), 3); int sum = 0; auto accumulate_sum = [&](int x) { sum += x; }; - p.invoke>(&accumulate_sum); + p.invoke>(&accumulate_sum); ASSERT_EQ(sum, 6); } TEST(ProxyInvocationTests, TestMultipleDispatches_Duplicated) { - using SomeCombination = std::tuple, std::tuple>>; - struct DuplicatedIterableFacade : pro::facade< - ForEach, SomeCombination, ForEach, GetSize, GetSize> {}; - static_assert(sizeof(pro::details::facade_traits::meta_type) == - sizeof(pro::details::facade_traits>::meta_type)); + using SomeCombination = std::tuple, std::tuple>>; + struct DuplicatedIterable : pro::facade< + 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 }; - pro::proxy p = &l; - ASSERT_EQ(p.invoke(), 3); + pro::proxy p = &l; + ASSERT_EQ(p.invoke(), 3); int sum = 0; auto accumulate_sum = [&](int x) { sum += x; }; - p.invoke>(&accumulate_sum); + p.invoke>(&accumulate_sum); ASSERT_EQ(sum, 6); } TEST(ProxyInvocationTests, TestRecursiveDefinition) { std::list l = { 1, 2, 3 }; - pro::proxy> p = &l; - ASSERT_EQ(p.invoke(), 3); + pro::proxy> p = &l; + ASSERT_EQ(p.invoke(), 3); int sum = 0; auto accumulate_sum = [&](int x) { sum += x; }; - p.invoke>(&accumulate_sum); + p.invoke>(&accumulate_sum); ASSERT_EQ(sum, 6); - p.invoke>(4).invoke>(5).invoke>(6); - ASSERT_EQ(p.invoke(), 6); + p.invoke>(4).invoke>(5).invoke>(6); + ASSERT_EQ(p.invoke(), 6); sum = 0; - p.invoke>(&accumulate_sum); + p.invoke>(&accumulate_sum); ASSERT_EQ(sum, 21); } TEST(ProxyInvocationTests, TestOverloadResolution) { - struct TestFacade : CallableFacade {}; std::vector side_effect; auto p = pro::make_proxy([&](auto&&... args) diff --git a/tests/proxy_lifetime_tests.cpp b/tests/proxy_lifetime_tests.cpp index 1790358..994ac1c 100644 --- a/tests/proxy_lifetime_tests.cpp +++ b/tests/proxy_lifetime_tests.cpp @@ -7,7 +7,7 @@ namespace { -struct TestFacade : pro::facade { +struct TestFacade : pro::facade { static constexpr auto minimum_copyability = pro::constraint_level::nontrivial; }; diff --git a/tests/utils.h b/tests/utils.h index 23ce976..412dc73 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -80,14 +80,12 @@ class LifetimeTracker { std::vector ops_; }; -namespace details { +namespace poly { using std::to_string; DEFINE_FREE_DISPATCH(ToString, to_string, std::string()); -} // namespace details - -using details::ToString; +} // namespace poly } // namespace utils