Register event handlers with `shared_ptr` and `weak_ptr` (#1330)

This commit is contained in:
Charles Milette 2023-07-10 18:16:10 -04:00 коммит произвёл GitHub
Родитель 297454ee28
Коммит 953d65cc85
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 180 добавлений и 3 удалений

Просмотреть файл

@ -2446,6 +2446,8 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
template <typename O, typename M> %(O* object, M method); template <typename O, typename M> %(O* object, M method);
template <typename O, typename M> %(com_ptr<O>&& object, M method); template <typename O, typename M> %(com_ptr<O>&& object, M method);
template <typename O, typename M> %(weak_ref<O>&& object, M method); template <typename O, typename M> %(weak_ref<O>&& object, M method);
template <typename O, typename M> %(std::shared_ptr<O>&& object, M method);
template <typename O, typename M> %(std::weak_ptr<O>&& object, M method);
auto operator()(%) const; auto operator()(%) const;
}; };
)"; )";
@ -2462,6 +2464,8 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
type_name, type_name,
type_name, type_name,
type_name, type_name,
type_name,
type_name,
bind<write_consume_params>(signature)); bind<write_consume_params>(signature));
} }
@ -2523,6 +2527,14 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
%([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } })
{ {
} }
template <%> template <typename O, typename M> %<%>::%(std::shared_ptr<O>&& object, M method) :
%([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); })
{
}
template <%> template <typename O, typename M> %<%>::%(std::weak_ptr<O>&& object, M method) :
%([o = std::move(object), method](auto&&... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } })
{
}
template <%> auto %<%>::operator()(%) const template <%> auto %<%>::operator()(%) const
{% {%
check_hresult((*(impl::abi_t<%<%>>**)this)->Invoke(%));% check_hresult((*(impl::abi_t<%<%>>**)this)->Invoke(%));%
@ -2562,6 +2574,16 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
bind<write_generic_typenames>(generics), bind<write_generic_typenames>(generics),
type_name, type_name,
bind_list(", ", generics), bind_list(", ", generics),
type_name,
type_name,
bind<write_generic_typenames>(generics),
type_name,
bind_list(", ", generics),
type_name,
type_name,
bind<write_generic_typenames>(generics),
type_name,
bind_list(", ", generics),
bind<write_consume_params>(signature), bind<write_consume_params>(signature),
bind<write_consume_return_type>(signature, true), bind<write_consume_return_type>(signature, true),
type_name, type_name,
@ -2591,6 +2613,14 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
%([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } })
{ {
} }
template <typename O, typename M> %::%(std::shared_ptr<O>&& object, M method) :
%([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); })
{
}
template <typename O, typename M> %::%(std::weak_ptr<O>&& object, M method) :
%([o = std::move(object), method](auto&&... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } })
{
}
inline auto %::operator()(%) const inline auto %::operator()(%) const
{% {%
check_hresult((*(impl::abi_t<%>**)this)->Invoke(%));% check_hresult((*(impl::abi_t<%>**)this)->Invoke(%));%
@ -2615,6 +2645,12 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
type_name, type_name,
type_name, type_name,
type_name, type_name,
type_name,
type_name,
type_name,
type_name,
type_name,
type_name,
bind<write_consume_params>(signature), bind<write_consume_params>(signature),
bind<write_consume_return_type>(signature, true), bind<write_consume_return_type>(signature, true),
type_name, type_name,

Просмотреть файл

@ -179,6 +179,16 @@ namespace winrt::impl
{ {
} }
template <typename O, typename M> delegate_base(std::shared_ptr<O>&& object, M method) :
delegate_base([o = std::move(object), method](auto&& ... args) { return ((*o).*(method))(args...); })
{
}
template <typename O, typename M> delegate_base(std::weak_ptr<O>&& object, M method) :
delegate_base([o = std::move(object), method](auto&& ... args) { if (auto s = o.lock()) { ((*s).*(method))(args...); } })
{
}
auto operator()(Args const& ... args) const auto operator()(Args const& ... args) const
{ {
return (*(variadic_delegate_abi<R, Args...> * *)this)->invoke(args...); return (*(variadic_delegate_abi<R, Args...> * *)this)->invoke(args...);

Просмотреть файл

@ -31,6 +31,25 @@ namespace
} }
}; };
template <typename Sender, typename Args>
struct ObjectStd : std::enable_shared_from_this<ObjectStd<Sender, Args>>
{
~ObjectStd()
{
destroyed = true;
}
void StrongHandler(Sender const&, Args const&)
{
++strong_count;
}
void WeakHandler(Sender const&, Args const&)
{
++weak_count;
}
};
struct ReturnObject : implements<ReturnObject, IInspectable> struct ReturnObject : implements<ReturnObject, IInspectable>
{ {
~ReturnObject() ~ReturnObject()
@ -44,8 +63,21 @@ namespace
} }
}; };
struct ReturnObjectStd : std::enable_shared_from_this<ReturnObjectStd>
{
~ReturnObjectStd()
{
destroyed = true;
}
int Handler(int a, int b)
{
return a + b;
}
};
template <typename Delegate, typename Sender, typename Args> template <typename Delegate, typename Sender, typename Args>
void test_delegate() void test_delegate_winrt()
{ {
auto object = make_self<Object<Sender, Args>>(); auto object = make_self<Object<Sender, Args>>();
@ -81,6 +113,51 @@ namespace
weak({}, {}); weak({}, {});
REQUIRE(weak_count == 2); REQUIRE(weak_count == 2);
} }
template <typename Delegate, typename Sender, typename Args>
void test_delegate_std()
{
auto object = std::make_shared<ObjectStd<Sender, Args>>();
Delegate strong{ object->shared_from_this(), &ObjectStd<Sender, Args>::StrongHandler };
Delegate weak{ object->weak_from_this(), &ObjectStd<Sender, Args>::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);
}
template <typename Delegate, typename Sender, typename Args>
void test_delegate()
{
test_delegate_winrt<Delegate, Sender, Args>();
test_delegate_std<Delegate, Sender, Args>();
}
} }
TEST_CASE("delegate_weak_strong") TEST_CASE("delegate_weak_strong")
@ -111,8 +188,10 @@ TEST_CASE("delegate_weak_strong")
// scenarios such as callbacks so weak support isn't interesting anyway, but it does work with get_strong. // scenarios such as callbacks so weak support isn't interesting anyway, but it does work with get_strong.
auto object = make_self<ReturnObject>(); auto object = make_self<ReturnObject>();
Component::TwoArgDelegateReturn strong{ object->get_strong(), &ReturnObject::Handler }; Component::TwoArgDelegateReturn strong{ object->get_strong(), &ReturnObject::Handler };
REQUIRE(5 == strong(2, 3)); REQUIRE(5 == strong(2, 3));
auto objectStd = std::make_shared<ReturnObjectStd>();
Component::TwoArgDelegateReturn strongStd{ objectStd->shared_from_this(), &ReturnObjectStd::Handler };
REQUIRE(5 == strongStd(2, 3));
} }

Просмотреть файл

@ -40,6 +40,20 @@ namespace
return L"Object"; return L"Object";
} }
}; };
struct ObjectStd : std::enable_shared_from_this<ObjectStd>
{
int& m_count;
ObjectStd(int& count) : m_count(count)
{
}
void Callback()
{
++m_count;
}
};
} }
TEST_CASE("variadic_delegate") TEST_CASE("variadic_delegate")
@ -100,6 +114,44 @@ TEST_CASE("variadic_delegate")
REQUIRE(count == 2); // Unchanged REQUIRE(count == 2); // Unchanged
} }
// shared_from_this
{
int count{};
auto object = std::make_shared<ObjectStd>(count);
delegate<> up{ object->shared_from_this(), &ObjectStd::Callback };
REQUIRE(count == 0);
up();
REQUIRE(count == 1);
up();
REQUIRE(count == 2);
object = nullptr;
up();
REQUIRE(count == 3);
}
// weak_from_this
{
int count{};
auto object = std::make_shared<ObjectStd>(count);
delegate<> up{ object->weak_from_this(), &ObjectStd::Callback };
REQUIRE(count == 0);
up();
REQUIRE(count == 1);
up();
REQUIRE(count == 2);
object = nullptr;
up();
REQUIRE(count == 2); // Unchanged
}
// Mixed arguments // Mixed arguments
{ {
int count{}; int count{};