{"id":108266,"date":"2023-05-31T07:00:00","date_gmt":"2023-05-31T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108266"},"modified":"2023-05-31T06:52:14","modified_gmt":"2023-05-31T13:52:14","slug":"20230531-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230531-00\/?p=108266","title":{"rendered":"C++\/WinRT event handlers that are lambdas with weak pointers to the parent class, part 1"},"content":{"rendered":"<p>C++\/WinRT provides multiple ways of auto-implementing delegates.<\/p>\n<pre>\/\/ Function pointer\r\ne.add(&amp;MyClass::StaticMemberFunction);\r\n\r\n\/\/ Raw pointer + non-static member function pointer\r\ne.add({ this, &amp;MyClass::MemberFunction });\r\n\r\n\/\/ Strong pointer + non-static member function pointer\r\ne.add({ get_strong(), &amp;MyClass::MemberFunction });\r\n\r\n\/\/ Weak pointer + non-static member function pointer\r\ne.add({ get_weak(), &amp;MyClass::MemberFunction });\r\n\r\n\/\/ Lambda\r\ne.add(lambda);\r\n<\/pre>\n<p>Internally, all of the constructors are just <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/blob\/cea2e4121ea533653a9e99c50158e05b4d9af6b6\/strings\/base_delegate.h#L164\"> shorthand for lambdas<\/a>:<\/p>\n<pre>template &lt;typename F&gt; delegate_base(F* handler) :\r\n    delegate_base(\r\n        [=](auto&amp;&amp; ... args)\r\n        { return handler(args...); }) {}\r\n\r\ntemplate &lt;typename O, typename M&gt; delegate_base(O* object, M method) :\r\n    delegate_base(\r\n        [=](auto&amp;&amp; ... args)\r\n        { return ((*object).*(method))(args...); }) {}\r\n\r\ntemplate &lt;typename O, typename M&gt; delegate_base(com_ptr&lt;O&gt;&amp;&amp; object, M method) :\r\n    delegate_base(\r\n        [o = std::move(object), method](auto&amp;&amp; ... args)\r\n        { return ((*o).*(method))(args...); }) {}\r\n\r\ntemplate &lt;typename O, typename M&gt; delegate_base(winrt::weak_ref&lt;O&gt;&amp;&amp; object, M method) :\r\n    delegate_base(\r\n        [o = std::move(object), method](auto&amp;&amp; ... args)\r\n        { if (auto s = o.get()) { ((*s).*(method))(args...); } }) {}\r\n<\/pre>\n<p>One pattern that is not covered by the above is the case of a parameterized callback.<\/p>\n<pre>void MyClass::OnSomething(IInspectable const&amp; sender,\r\n                          IInspectable const&amp; args,\r\n                          <span style=\"border: solid 1px currentcolor;\">int otherData<\/span>)\r\n{\r\n    ...\r\n}\r\n<\/pre>\n<p>You want to register <code>OnSomething<\/code> as an event handler in multiple places, with each place having a different <code>otherData<\/code>.<\/p>\n<pre>\/\/ When the Widget.Something event occurs,\r\n\/\/ call the OnSomething method with a specific otherData.\r\nvoid MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Doesn't work\r\n    widget.Something({ get_weak(), &amp;MyClass::OnSomething, otherData });\r\n}\r\n<\/pre>\n<p>This doesn&#8217;t work because a pointer to member function doesn&#8217;t have a place to stick extra information. Instead, you have to use a lambda:<\/p>\n<pre>\/\/ When the Widget.Something event occurs,\r\n\/\/ call the OnSomething method with a specific otherData.\r\nvoid MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Raw pointer version\r\n    widget.Something(\r\n        [this, otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            this-&gt;OnSomething(sender, args, otherData);\r\n        });\r\n}\r\n<\/pre>\n<p>This version captures <code>this<\/code> as a raw pointer, but we can teach it about the other two patterns:<\/p>\n<pre>void MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Strong pointer version\r\n    widget.Something(\r\n        [strong = get_strong(), otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            strong-&gt;OnSomething(sender, args, otherData);\r\n        });\r\n}\r\n<\/pre>\n<pre>void MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Weak pointer version\r\n    widget.Something(\r\n        [weak = get_weak(), otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            if (auto strong = weak.get()) {\r\n                strong-&gt;OnSomething(sender, args, otherData);\r\n            }\r\n        });\r\n}\r\n<\/pre>\n<p>In practice, the work of the lambda is often inlined, so you end up with the following:<\/p>\n<pre>\/\/ When the Widget.Something event occurs,\r\n\/\/ call the OnSomething method with a specific otherData.\r\nvoid MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Raw pointer version\r\n    widget.Something(\r\n        [this, otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            DoThing1(sender);\r\n            DoThing2(args);\r\n            DoThing3(otherData);\r\n        });\r\n}\r\n\r\nvoid MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Strong pointer version, with bonus \"this\" capture\r\n    \/\/ for convenience.\r\n    widget.Something(\r\n        [strong = get_strong(), this, otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            DoThing1(sender);\r\n            DoThing2(args);\r\n            DoThing3(otherData);\r\n        });\r\n}\r\n<\/pre>\n<pre>void MyClass::RegisterSomething(int otherData)\r\n{\r\n    \/\/ Weak pointer version\r\n    widget.Something(\r\n        [weak = get_weak(), this, otherData]\r\n        (auto&amp;&amp; sender, auto&amp;&amp; args)\r\n        {\r\n            if (auto strong = weak.get()) {\r\n                DoThing1(sender);\r\n                DoThing2(args);\r\n                DoThing3(otherData);\r\n            }\r\n        });\r\n}\r\n<\/pre>\n<p>In the first two cases, you just write a lambda that does what you want to do. The third case, however, is annoying: You have to perform an extra step to resolve the weak reference. This extra step was handled by the C++\/WinRT &#8220;weak pointer + non-static member function pointer&#8221; wrapper, but the lambda wrapper doesn&#8217;t provide the same courtesy.<\/p>\n<p>Next time, we&#8217;ll look at extending the courtesy to lambdas.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Assessing the state of affairs.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108266","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Assessing the state of affairs.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108266","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108266"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108266\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108266"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108266"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108266"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}