{"id":108487,"date":"2023-07-26T07:00:00","date_gmt":"2023-07-26T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108487"},"modified":"2023-07-26T07:28:22","modified_gmt":"2023-07-26T14:28:22","slug":"20230726-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230726-00\/?p=108487","title":{"rendered":"On the various ways of creating Windows Runtime delegates in C++\/WinRT and C++\/CX"},"content":{"rendered":"<p>Windows Runtime delegates are a language-independent way of representing callbacks. We&#8217;ll start with C++\/WinRT. Let&#8217;s say that <code>Delegate\u00adType<\/code> is a delegate type.<\/p>\n<pre>template&lt;typename L&gt;\r\nDelegateType(L handler) :\r\n    delegate_base(make(std::forward&lt;L&gt;(handler)))\r\n{}\r\n\r\ntemplate&lt;typename F&gt;\r\nDelegateType(F* handler) :\r\n    delegate_base([=](auto&amp;&amp;... args)\r\n    { return handler(args...); })\r\n{}\r\n\r\ntemplate&lt;typename O, typename M&gt;\r\nDelegateType(O* o, M method) :\r\n    delegate_base([=](auto&amp;&amp;... args)\r\n    { return (o-&gt;*method)(args...); })\r\n{}\r\n\r\ntemplate&lt;typename O, typename M&gt;\r\nDelegateType(com_ptr&lt;O&gt;&amp;&amp; o, M method) :\r\n    delegate_base([=](auto&amp;&amp;... args)\r\n    { return (o-&gt;*method)(args...); })\r\n{}\r\n\r\ntemplate&lt;typename O, typename M&gt;\r\nDelegateType(weak_ref&lt;O&gt;&amp;&amp; o, M method) :\r\n    delegate_base([o = std::move(o), method](auto&amp;&amp;... args)\r\n    { if (auto s = o.get()) (s-&gt;*method)(args...); })\r\n{}\r\n<\/pre>\n<p>In words:<\/p>\n<ul>\n<li>Construct from a lambda. The delegate parameters are passed to the lambda.<\/li>\n<li>Construct from a function pointer. The delegate parameters are passed to the function.<\/li>\n<li>Construct from a raw pointer and a pointer to member function. The member function is invoked on the object with the delegate parameters.<\/li>\n<li>Construct from a <code>com_ptr<\/code> and a pointer to member function. The member function is invoked on the object with the delegate parameters.<\/li>\n<li>Construct from a <code>weak_ptr<\/code> and a pointer to member function. When the delegate is invoked, try to promote the weak pointer to strong, and if successful, the member function is invoked on the object with the delegate parameters.<\/li>\n<\/ul>\n<p>All C++\/WinRT delegates are deemed free-threaded. The delegate runs on whatever thread the invoker chooses.<\/p>\n<p>C++\/WinRT provides three different &#8220;pointer to member function&#8221; flavors.<\/p>\n<ul>\n<li>Raw pointer: It is your responsibility to ensure that the pointed-to object is not destroyed before the delegate is invoked.<\/li>\n<li><code>com_ptr<\/code>: This retains a strong reference to the object, so be mindful of potential circular references.<\/li>\n<li><code>weak_ref<\/code>: Best of both worlds.<\/li>\n<\/ul>\n<p>I hope you&#8217;re not still using C++\/CX, but if you are, here are the ways of constructing a C++\/CX delegate.<\/p>\n<pre>template&lt;typename L&gt;\r\nDelegateType(L handler, CallbackContext context = CallbackContext::Any);\r\n\r\ntemplate&lt;typename O&gt;\r\nDelegateType(O^ o, M method, CallbackContext context = CallbackContext::Any,\r\n    bool strong = false);\r\n<\/pre>\n<p>In words:<\/p>\n<ul>\n<li>Construct from anything that you can use <code>operator()<\/code> with. This includes lambdas, <code>std::function<\/code>, and function pointers. The callable is invoked with the delegate parameters.<\/li>\n<li>Construct from a ref pointer to an object and a pointer to member function. The member function is invoked on the object with the delegate parameters.<\/li>\n<\/ul>\n<p>The <code>context<\/code> parameter lets you customize the COM apartment context in which the callback is made. It defaults to <code>Any<\/code>, which means that the delegate is free-threaded and runs on whatever thread the invoker chooses. If you specify <code>Same<\/code>, then the delegate is non-agile, and the invoke will be marshalled into the apartment in which in which it was created.<\/p>\n<p>In the case where you pass a pointer to member function, you also have the option of choosing whether the <code>o<\/code> is captured by weak reference (default) or strong reference (<code>strong = true<\/code>).<\/p>\n<p>There is no option for passing a raw pointer. If you want the handler to be a plain C++ object, you can use a custom lambda.<\/p>\n<p>Here is a table.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; font-size: 80%;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Scenario<\/th>\n<th>C++\/WinRT<\/th>\n<th>C++\/CX<\/th>\n<\/tr>\n<tr>\n<th>Assumptions<\/th>\n<td valign=\"baseline\">\n<pre>\/\/ IDL\r\nruntimeclass S { \/* ... *\/ } \/* sender *\/\r\nruntimeclass EA { \/* ... *\/ } \/* event args *\/\r\n\r\ndelegate void D(S sender, EA e);\r\n\r\n\/\/ Code-behind\r\n\/* Plain C++ class *\/\r\nstruct C {\r\n  void OnEvent(S const&amp;, EA const&amp;);\r\n  void CreateDelegate();\r\n};\r\n\r\n\/* C++\/WinRT class *\/\r\nstruct RC : winrt::implements&lt;RC, ...&gt;\r\n{\r\n  void OnEvent(S const&amp;, EA const&amp;);\r\n  void CreateDelegate();\r\n};\r\n\r\nauto lambda = [\/* captures *\/]\r\n  (auto&amp;&amp; s, auto&amp;&amp; e)\r\n  {\r\n    \/* ... *\/\r\n  };\r\n\r\nstd::function&lt;void(S, EA)&gt; sf;\r\n\r\nvoid f(S const&amp;, EA const&amp;);\r\n<\/pre>\n<\/td>\n<td valign=\"baseline\">\n<pre>ref class S { \/* ... *\/ }; \/* sender *\/\r\nref class EA { \/* ... *\/ }; \/* event args *\/\r\n\r\ndelegate void D(S^ sender, EA^ e);\r\n\r\n\r\n\/* Plain C++ class *\/\r\nstruct C {\r\n  void OnEvent(S^, EventArgs^);\r\n  void CreateDelegate();\r\n};\r\n\r\n\/* ref class *\/\r\nref class RC\r\n{\r\n  void OnEvent(S^, EventArgs^);\r\n  void CreateDelegate();\r\n};\r\n\r\nauto lambda = [\/* captures *\/]\r\n  (S^ sender, EA^ e)\r\n  {\r\n    \/* ... *\/\r\n  };\r\n\r\nstd::function&lt;void(S^, EA^)&gt; sf;\r\n\r\nvoid f(Sender^ sender, EventArgs^ e);\r\n<\/pre>\n<\/td>\n<\/tr>\n<tr>\n<th>Lambda<\/th>\n<td><code>D(lambda)<\/code><br \/>\n<code>\/* always agile *\/<\/code><br \/>\n(Lambda must be movable)<\/td>\n<td><code>ref new D(lambda) \/\/ agile<\/code><br \/>\n<code>ref new D(lambda, Same) \/\/ non-agile<\/code><br \/>\n(Lambda must be copyable)<\/td>\n<\/tr>\n<tr>\n<th><code>std::function<\/code><\/th>\n<td><code>D(sf)<\/code><br \/>\n<code>\/* always agile *\/<\/code><\/td>\n<td><code>ref new D(sf) \/\/ agile<\/code><br \/>\n<code>ref new D(sf, Same) \/\/ non-agile<\/code><\/td>\n<\/tr>\n<tr>\n<th>Free function pointer<\/th>\n<td><code>D(f)<\/code><br \/>\n<code>\/* always agile *\/<\/code><\/td>\n<td><code>ref new D(f) \/\/ agile<\/code><br \/>\n<code>ref new D(f, Same) \/\/ non-agile<\/code><\/td>\n<\/tr>\n<tr>\n<th>Raw pointer +<br \/>\nmethod<\/th>\n<td><code>void C::CreateDelegate() {<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(this, &amp;C::OnEvent);<\/code><br \/>\n<code>\u00a0\u00a0\/* always agile *\/<\/code><br \/>\n<code>}<\/code><\/td>\n<td>Not available (see below)<\/td>\n<\/tr>\n<tr>\n<th>Strong pointer +<br \/>\nmethod<\/th>\n<td><code>void RC::CreateDelegate() {<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(get_strong(), &amp;C::OnEvent);<\/code><br \/>\n<code>\u00a0\u00a0\/* always agile *\/<\/code><br \/>\n<code>}<\/code><\/td>\n<td><code>void RC::CreateDelegate() {<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(this, &amp;C::OnEvent, Any, true); \/\/ agile<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(this, &amp;C::OnEvent, Same, true); \/\/ non-agile<\/code><br \/>\n<code>}<\/code><\/td>\n<\/tr>\n<tr>\n<th>Weak pointer +<br \/>\nmethod<\/th>\n<td><code>void RC::CreateDelegate() {<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(get_weak(), &amp;C::OnEvent);<\/code><br \/>\n<code>\u00a0\u00a0\/* always agile *\/<\/code><br \/>\n<code>}<\/code><\/td>\n<td><code>void RC::CreateDelegate() {<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(this, &amp;C::OnEvent); \/\/ agile<\/code><br \/>\n<code>\u00a0\u00a0auto d = D(this, &amp;C::OnEvent, Same); \/\/ non-agile<\/code><br \/>\n<code>}<\/code><\/td>\n<\/tr>\n<tr>\n<th>If weak pointer<br \/>\nfails to resolve<\/th>\n<td>Ignore<\/td>\n<td>Throw <code>DisconnectedException<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Neither C++\/WinRT delegates nor C++\/CX delegates have native support for C++ weak and shared pointers, and C++\/CX delegates lack support for raw pointers. Fortunately, you can make up any missing features by using a lambda.<\/p>\n<pre>\/\/ C++\/WinRT\r\n\r\nstruct SC : std::enable_shared_from_this&lt;SC&gt;\r\n{\r\n  void OnEvent(S const&amp;, EA const&amp;);\r\n  void CreateDelegate() {\r\n    \/\/ with weak_ptr\r\n    auto d = D([weak = weak_from_this()]\r\n               (auto&amp;&amp; s, auto&amp;&amp; e) {\r\n      if (auto shared = weak.lock()) shared-&gt;OnEvent(s, e);\r\n    });\r\n\r\n    \/\/ with shared_ptr\r\n    auto d = D([shared = shared_from_this()]\r\n               (auto&amp;&amp; s, auto&amp;&amp; e) {\r\n      shared-&gt;OnEvent(s, e);\r\n    });\r\n  }\r\n};\r\n\r\n\/\/ C++\/CX\r\n\r\nstruct SC : std::enable_shared_from_this&lt;SC&gt;\r\n{\r\n  void OnEvent(S^, EA^);\r\n  void CreateDelegate() {\r\n    \/\/ with weak_ptr\r\n    auto d = D([weak = weak_from_this()]\r\n               (S^ s, EA^ e) {\r\n      if (auto shared = weak.lock()) shared-&gt;OnEvent(s, e);\r\n    });\r\n\r\n    \/\/ with shared_ptr\r\n    auto d = D([shared = shared_from_this()]\r\n               (S^ s, EA^ e) {\r\n      shared-&gt;OnEvent(s, e);\r\n    });\r\n\r\n    \/\/ with raw pointer\r\n    auto d = D([this]\r\n               (S^ s, EA^ e) {\r\n      this-&gt;OnEvent(s, e);\r\n    });\r\n  }\r\n};\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Comparing and contrasting.<\/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-108487","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Comparing and contrasting.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108487","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=108487"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108487\/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=108487"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108487"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108487"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}