diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index a5c306fa..3c77269c 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -2488,9 +2488,9 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable template %(F* function); template %(O* object, M method); template %(com_ptr&& object, M method); - template %(weak_ref&& object, M method); + template %(weak_ref&& object, LM&& lambda_or_method); template %(std::shared_ptr&& object, M method); - template %(std::weak_ptr&& object, M method); + template %(std::weak_ptr&& object, LM&& lambda_or_method); auto operator()(%) const; }; )"; @@ -2566,16 +2566,22 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template <%> template %<%>::%(weak_ref&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template <%> template %<%>::%(weak_ref&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } template <%> template %<%>::%(std::shared_ptr&& object, M method) : %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template <%> template %<%>::%(std::weak_ptr&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } }) + template <%> template %<%>::%(std::weak_ptr&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } template <%> auto %<%>::operator()(%) const @@ -2652,16 +2658,22 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template %::%(weak_ref&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template %::%(weak_ref&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } template %::%(std::shared_ptr&& object, M method) : %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template %::%(std::weak_ptr&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } }) + template %::%(std::weak_ptr&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } inline auto %::operator()(%) const diff --git a/strings/base_delegate.h b/strings/base_delegate.h index 05742b61..8902b3cc 100644 --- a/strings/base_delegate.h +++ b/strings/base_delegate.h @@ -180,8 +180,11 @@ namespace winrt::impl { } - template delegate_base(winrt::weak_ref&& object, M method) : - delegate_base([o = std::move(object), method](auto&& ... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template delegate_base(winrt::weak_ref&& object, LM&& lambda_or_method) : + delegate_base([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + }}) { } @@ -190,8 +193,11 @@ namespace winrt::impl { } - template delegate_base(std::weak_ptr&& object, M method) : - delegate_base([o = std::move(object), method](auto&& ... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } }) + template delegate_base(std::weak_ptr&& object, LM&& lambda_or_method) : + delegate_base([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + }}) { } diff --git a/test/old_tests/UnitTests/delegate_weak_strong.cpp b/test/old_tests/UnitTests/delegate_weak_strong.cpp index 30b02672..84990c75 100644 --- a/test/old_tests/UnitTests/delegate_weak_strong.cpp +++ b/test/old_tests/UnitTests/delegate_weak_strong.cpp @@ -8,55 +8,85 @@ using namespace Windows::Foundation::Collections; namespace { - bool destroyed{}; - int strong_count{}; - int weak_count{}; + struct Counters + { + bool destroyed{}; + int strong_count{}; + int weak_count{}; + int weak_lambda_count{}; + + bool is_count(int value) + { + return strong_count == value && weak_count == value && weak_lambda_count == value; + } + }; template struct Object : implements, IInspectable> { + std::shared_ptr m_counters; + + Object(std::shared_ptr const& counters) : m_counters(counters) {} + + static auto make(std::shared_ptr const& counters) + { + return make_self(counters); + } + + auto strong() { return this->get_strong(); } + auto weak() { return this->get_weak(); } + ~Object() { - destroyed = true; + m_counters->destroyed = true; } void StrongHandler(Sender const&, Args const&) { - ++strong_count; + REQUIRE(!m_counters->destroyed); + ++m_counters->strong_count; } void WeakHandler(Sender const&, Args const&) { - ++weak_count; + REQUIRE(!m_counters->destroyed); + ++m_counters->weak_count; } }; template struct ObjectStd : std::enable_shared_from_this> { + std::shared_ptr m_counters; + + ObjectStd(std::shared_ptr const& counters) : m_counters(counters) {} + + static auto make(std::shared_ptr const& counters) + { + return std::make_shared(counters); + } + + auto strong() { return this->shared_from_this(); } + auto weak() { return this->weak_from_this(); } + ~ObjectStd() { - destroyed = true; + m_counters->destroyed = true; } void StrongHandler(Sender const&, Args const&) { - ++strong_count; + ++m_counters->strong_count; } void WeakHandler(Sender const&, Args const&) { - ++weak_count; + ++m_counters->weak_count; } }; struct ReturnObject : implements { - ~ReturnObject() - { - destroyed = true; - } - int Handler(int a, int b) { return a + b; @@ -65,91 +95,63 @@ namespace struct ReturnObjectStd : std::enable_shared_from_this { - ~ReturnObjectStd() - { - destroyed = true; - } - int Handler(int a, int b) { return a + b; } }; - template - void test_delegate_winrt() + template + void test_delegate_pattern() { - auto object = make_self>(); + auto counters = std::make_shared(); + auto object = Recipient::make(counters); - Delegate strong{ object->get_strong(), &Object::StrongHandler }; - Delegate weak{ object->get_weak(), &Object::WeakHandler }; + Delegate strong{ object->strong(), &Recipient::StrongHandler}; + Delegate weak{ object->weak(), &Recipient::WeakHandler }; + Delegate weak_lambda{ object->weak(),[counters](auto&&, auto&&) { + REQUIRE(!counters->destroyed); + ++counters->weak_lambda_count; + } }; - destroyed = false; - strong_count = 0; - weak_count = 0; - - // Both weak and strong handlers + // All handlers are active at this point strong({}, {}); weak({}, {}); - REQUIRE(strong_count == 1); - REQUIRE(weak_count == 1); + weak_lambda({}, {}); + REQUIRE(counters->is_count(1)); // Local 'object' strong reference is released object = nullptr; - // Still both since strong handler keeps object alive + // Still invoked since strong handler keeps object alive strong({}, {}); weak({}, {}); - REQUIRE(strong_count == 2); - REQUIRE(weak_count == 2); + weak_lambda({}, {}); + REQUIRE(counters->is_count(2)); - // ~Object is called since the strong delegate is destroyed - REQUIRE(!destroyed); + // ~Recipient is called since the strong delegate is destroyed + REQUIRE(!counters->destroyed); strong = nullptr; - REQUIRE(destroyed); + REQUIRE(counters->destroyed); // Weak delegate remains but no longer fires - REQUIRE(weak_count == 2); + // Strong delegate shouldn't fire either + REQUIRE(counters->is_count(2)); weak({}, {}); - REQUIRE(weak_count == 2); + weak_lambda({}, {}); + REQUIRE(counters->is_count(2)); + } + + template + void test_delegate_winrt() + { + test_delegate_pattern, Delegate>(); } template void test_delegate_std() { - auto object = std::make_shared>(); - - Delegate strong{ object->shared_from_this(), &ObjectStd::StrongHandler }; - Delegate weak{ object->weak_from_this(), &ObjectStd::WeakHandler }; - - destroyed = false; - strong_count = 0; - weak_count = 0; - - // Both weak and strong handlers - strong({}, {}); - weak({}, {}); - REQUIRE(strong_count == 1); - REQUIRE(weak_count == 1); - - // Local 'object' strong reference is released - object = nullptr; - - // Still both since strong handler keeps object alive - strong({}, {}); - weak({}, {}); - REQUIRE(strong_count == 2); - REQUIRE(weak_count == 2); - - // ~Object is called since the strong delegate is destroyed - REQUIRE(!destroyed); - strong = nullptr; - REQUIRE(destroyed); - - // Weak delegate remains but no longer fires - REQUIRE(weak_count == 2); - weak({}, {}); - REQUIRE(weak_count == 2); + test_delegate_pattern, Delegate>(); } template