{"id":108278,"date":"2023-06-02T07:00:00","date_gmt":"2023-06-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108278"},"modified":"2023-06-02T09:24:21","modified_gmt":"2023-06-02T16:24:21","slug":"20230602-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230602-00\/?p=108278","title":{"rendered":"C++\/WinRT event handlers that are lambdas with weak pointers to the parent class, part 3"},"content":{"rendered":"<p>Last time, <a title=\"C++\/WinRT event handlers that are lambdas with weak pointers to the parent class, part 2\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230601-00\/?p=108272\"> we created a helper function for creating a delegate from a weak pointer and a lambda<\/a>. The weak pointer in question was a C++\/WinRT weak pointer, but maybe you want to use this from a traditional C++ class, in which case what you have is not a <code>winrt::weak_ref<\/code> but rather a <code>std::weak_ptr<\/code>. Let&#8217;s adapt our function to work with <code>std::weak_ptr<\/code> too.<\/p>\n<p>Recall that we left off last time with this function:<\/p>\n<pre>template&lt;typename T, typename Handler&gt;\r\nauto weak_delegate(\r\n    winrt::weak_ref&lt;T&gt; weak,\r\n    Handler&amp;&amp; handler)\r\n{\r\n  return [weak = std::move(weak),\r\n          handler = std::forward&lt;Handler&gt;(handler)]\r\n    (auto&amp;&amp;... args) mutable\r\n    {\r\n      if (auto strong = weak.get()) {\r\n        handler(std::forward&lt;decltype(args)&gt;(args)...);\r\n      }\r\n    };\r\n}\r\n<\/pre>\n<p>The only difference that matters (for our purposes) between <code>winrt::weak_ref<\/code> and <code>std::weak_ptr<\/code> is that <code>winrt::weak_ref<\/code> uses the <code>get()<\/code> method to promote the weak reference to a strong reference, whereas the corresponding method on <code>std::weak_ptr<\/code> is called <code>lock()<\/code>.<\/p>\n<p>What we want to do is something weird like<\/p>\n<pre>template&lt;typename Weak, typename Handler&gt;\r\nauto weak_delegate(\r\n    Weak&amp;&amp; weak,\r\n    Handler&amp;&amp; handler)\r\n{\r\n  return [weak = std::forward&lt;Weak&gt;(weak),\r\n          handler = std::forward&lt;Handler&gt;(handler)]\r\n    (auto&amp;&amp;... args) mutable\r\n    {\r\n      <span style=\"border: solid 1px currentcolor;\">if constexpr (is_specialization_v&lt;Weak, std::weak_ptr&gt;) {<\/span>\r\n        auto strong = weak.lock();\r\n      } else {\r\n        auto strong = weak.get();\r\n      }\r\n      if (strong) {\r\n        handler(std::forward&lt;decltype(args)&gt;(args)...);\r\n      }\r\n    };\r\n}\r\n<\/pre>\n<p>But that doesn&#8217;t work because the scope of <code>strong<\/code> is wrong.\u00b9<\/p>\n<p>What we could do is turn it into a lambda:<\/p>\n<pre>template&lt;typename Weak, typename Handler&gt;\r\nauto weak_delegate(\r\n    Weak&amp;&amp; weak,\r\n    Handler&amp;&amp; handler)\r\n{\r\n  return [weak = std::forward&lt;Weak&gt;(weak),\r\n          handler = std::forward&lt;Handler&gt;(handler)]\r\n    (auto&amp;&amp;... args) mutable\r\n    {\r\n      <span style=\"border: solid 1px currentcolor;\">auto strong = [&amp;] {<\/span>\r\n        if constexpr (is_specialization_v&lt;Weak, std::weak_ptr&gt;) {\r\n          <span style=\"border: solid 1px currentcolor;\">return<\/span> weak.lock();\r\n        } else {\r\n          <span style=\"border: solid 1px currentcolor;\">return<\/span> weak.get();\r\n        }\r\n      <span style=\"border: solid 1px currentcolor;\">}()<\/span>;\r\n      if (strong) {\r\n        handler(std::forward&lt;decltype(args)&gt;(args)...);\r\n      }\r\n    };\r\n}\r\n<\/pre>\n<p>Now, writing that <code>is_specialization_v<\/code> thingie is going to be a bit of an annoyance, but it turns out we don&#8217;t need to do it. We can let function overloading do the work.<\/p>\n<pre>template&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(std::weak_ptr&lt;Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.lock();\r\n}\r\n\r\ntemplate&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(winrt::weak_ref&lt;Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.get();\r\n}\r\n\r\ntemplate&lt;typename Weak, typename Handler&gt;\r\nauto weak_delegate(\r\n    Weak&amp;&amp; weak,\r\n    Handler&amp;&amp; handler)\r\n{\r\n  return [weak = std::forward&lt;Weak&gt;(weak),\r\n          handler = std::forward&lt;Handler&gt;(handler)]\r\n    (auto&amp;&amp;... args) mutable\r\n    {\r\n      <span style=\"border: solid 1px currentcolor;\">if (auto strong = try_resolve_weak_pointer_to_strong(weak))<\/span> {\r\n        handler(std::forward&lt;decltype(args)&gt;(args)...);\r\n      }\r\n    };\r\n}\r\n<\/pre>\n<p>The <code>try_<wbr \/>resolve_<wbr \/>weak_<wbr \/>pointer_<wbr \/>to_<wbr \/>strong<\/code> functions use a trick we looked at <a title=\"On writing functions that accept any specialization of a C++ template type\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230529-00\/?p=108259\"> a little while ago<\/a>.<\/p>\n<p>Now you can use <code>weak_<wbr \/>delegate<\/code> with C++ standard library weak pointers, as well as C++\/WinRT weak references.<\/p>\n<pre>struct MyClass : std::enable_shared_from_this&lt;MyClass&gt;\r\n{\r\n    ...\r\n\r\n    void RegisterSomething(int otherData)\r\n    {\r\n      widget.Something(<span style=\"border: solid 1px currentcolor;\">weak_delegate(weak_from_this(),<\/span>\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        }<span style=\"border: solid 1px currentcolor;\">)<\/span>);\r\n    }\r\n};\r\n<\/pre>\n<p>A benefit of delegating the &#8220;resolve a weak pointer to a strong pointer&#8221; decision to a function overload is that you can extend support to other weak pointer libraries by adding new overloads.<\/p>\n<pre>\/\/ WRL WeakRef\r\nauto try_resolve_weak_pointer_to_strong(::Microsoft::WRL::WeakRef const&amp; weak)\r\n{\r\n    ::Microsoft::WRL::ComPtr&lt;IInspectable&gt; o;\r\n    weak.As(&amp;o);\r\n    return o;\r\n}\r\n\r\n\/\/ wil com_weak_ptr\r\ntemplate&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(\r\n    wil::com_ptr&lt;IWeakReference, Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.try_copy&lt;IUnknown&gt;();\r\n}\r\n\r\n\/\/ Unreal Engine TWeakPtr\r\ntemplate&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(TWeakPtr&lt;Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.Pin();\r\n}\r\n\r\n\/\/ Unreal Engine TWeakObjectPtr\r\ntemplate&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(TWeakObjectPtr&lt;Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.Get();\r\n}\r\n\r\n\/\/ Qt QWeakPointer\r\ntemplate&lt;typename... Extra&gt;\r\nauto try_resolve_weak_pointer_to_strong(QWeakPointer&lt;Extra...&gt; const&amp; weak)\r\n{\r\n    return weak.toStrongRef();\r\n}\r\n<\/pre>\n<p>Note that we are using the <code>Extra...<\/code> technique for <a title=\"On writing functions that accept any specialization of a C++ template type\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230529-00\/?p=108259\"> accepting any specialization of a C++ template type<\/a>.\u00b2<\/p>\n<p>The case of <code>wil<\/code> is a little tricky because <code>com_weak_ptr<\/code> is itself a template alias. We have to use the name of the base template type rather than the type alias.<\/p>\n<p>\u00b9 If you decide to deepen the <a title=\"The sad history of Visual Studios custom __if_exists keyword\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190828-00\/?p=102812\"> sad history of Visual Studios custom <code>__if_exists<\/code> keyword<\/a>, you could write<\/p>\n<pre>template&lt;typename Weak, typename Handler&gt;\r\nauto weak_delegate(\r\n    Weak&amp;&amp; weak,\r\n    Handler&amp;&amp; handler)\r\n{\r\n  return [weak = std::forward&lt;Weak&gt;(weak),\r\n          handler = std::forward&lt;Handler&gt;(handler)]\r\n    (auto&amp;&amp;... args) mutable\r\n    {\r\n      <span style=\"border: solid 1px currentcolor;\">__if_exists(Weak::get)<\/span> {\r\n        auto strong = weak.get();\r\n      }\r\n      <span style=\"border: solid 1px currentcolor;\">__if_exists(Weak::lock)<\/span> {\r\n        auto strong = weak.lock();\r\n      }\r\n      if (strong) {\r\n        handler(std::forward&lt;decltype(args)&gt;(args)...);\r\n      }\r\n    };\r\n}\r\n<\/pre>\n<p>But please, don&#8217;t do that. Use SFINAE instead.<\/p>\n<p>\u00b2 Others have noted that the <code>Extra...<\/code> trick doesn&#8217;t work for templates that take non-type template parameters or other templates, so maybe SFINAE is the way to go after all.<\/p>\n<pre>\/\/ winrt weak_ref\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.get())\r\n{\r\n    return weak.get();\r\n}\r\n\r\n\/\/ std weak_ptr\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.lock())\r\n{\r\n    return weak.lock();\r\n}\r\n\r\n\/\/ WRL WeakRef (no change)\r\nauto try_resolve_weak_pointer_to_strong(::Microsoft::WRL::WeakRef const&amp; weak)\r\n{\r\n    ::Microsoft::WRL::ComPtr&lt;IInspectable&gt; o;\r\n    weak.As(&amp;o);\r\n    return o;\r\n}\r\n\r\n\/\/ wil com_weak_ptr\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.try_copy&lt;IUnknown&gt;())\r\n{\r\n    return weak.try_copy&lt;IUnknown&gt;();\r\n}\r\n\r\n\/\/ Unreal Engine TWeakPtr\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.Pin())\r\n{\r\n    return weak.Pin();\r\n}\r\n\r\n\/\/ Unreal Engine TWeakObjectPtr\r\n\/\/ (Would also accidentally match WRL if not for the better overload above)\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.Get())\r\n{\r\n    return weak.Get();\r\n}\r\n\r\n\/\/ Qt QWeakPointer\r\ntemplate&lt;typename T&gt;\r\nauto try_resolve_weak_pointer_to_strong(T const&amp; weak)\r\n-&gt; decltype(weak.toStrongRef())\r\n{\r\n    return weak.toStrongRef();\r\n}\r\n<\/pre>\n<p>One of the risks of using SFINAE for duck typing is that you may detect things that you didn&#8217;t mean to. For example, here, we have a name collision: Both WRL WeakRef and Unreal Engine TWeakObjectPtr have a <code>Get<\/code> method, but they do different things. The WRL <code>Get<\/code> method returns the wrapped <code>IWeakReference*<\/code> (no resolving happens), whereas the Unreal Engine <code>Get<\/code> method resolves the weak pointer to a strong pointer. We have to make sure that the WRL specialization gets picked up for WRL WeakRef.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Extending to <CODE>std::weak_ptr<\/CODE>.<\/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-108278","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Extending to <CODE>std::weak_ptr<\/CODE>.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108278","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=108278"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108278\/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=108278"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108278"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108278"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}