{"id":110908,"date":"2025-02-26T07:00:00","date_gmt":"2025-02-26T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110908"},"modified":"2025-02-27T09:41:08","modified_gmt":"2025-02-27T17:41:08","slug":"20250226-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250226-00\/?p=110908","title":{"rendered":"C++\/WinRT implementation inheritance: Notes on <CODE>winrt::implements<\/CODE>, part 6"},"content":{"rendered":"<p>Last time, <a title=\"C++\/WinRT implementation inheritance: Notes on winrt::implements, part 5\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250225-00\/?p=110905\"> we were looking for a way to allow a <code>winrt::<wbr \/>implements<\/code>-based base class to defer implementation of a method to its derived class<\/a>. The problem is that if you use <code>winrt::<wbr \/>implements<\/code> to implement an interface, you have to implement all the methods. But you might also want to leave some of the methods for the derived class to implement in a manner of its choosing (for example, choosing whether the parameters are references or values, or allowing the parameters to be templated), which means that the virtual method approach doesn&#8217;t work. Something has to give.<\/p>\n<p>There are multiple solutions, depending on which requirement you want to weaken.<\/p>\n<p>You can weaken the &#8220;leave some of the methods for the derived class to implement&#8221; requirement by implementing it yourself but forwarding the call to the derived class via CRTP.<\/p>\n<pre>\/\/ A simple copy drop target provides no custom feedback\r\n\/\/ and accepts anything by copy.\r\n<span style=\"border: solid 1px currentcolor;\">template&lt;typename D&gt;<\/span>\r\nstruct SimpleCopyDropTarget :\r\n    winrt::implements&lt;SimpleCopyDropTarget<span style=\"border: solid 1px currentcolor;\">&lt;D&gt;<\/span>,\r\n    winrt::ICoreDropOperationTarget&gt;\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        EnterAsync(winrt::CoreDragInfo const&amp; info,\r\n                   winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        OverAsync(winrt::CoreDragInfo const&amp; info,\r\n                  winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncAction\r\n        LeaveAsync(winrt::CoreDragInfo const&amp;)\r\n    {\r\n        co_return;\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    DropAsync(winrt::CoreDragInfo const&amp; info)     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return static_cast&lt;D*&gt;(this)-&gt;                 <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        DropAsyncImpl(info);                       <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                  <\/span>\r\n\r\nprotected:\r\n    winrt::DataPackageOperation GetOperation(\r\n        winrt::CoreDragInfo const&amp; info)\r\n    {\r\n        return info.AllowedOperations() &amp;\r\n               winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n\r\nstruct Derived : winrt::implements&lt;\r\n    Derived,\r\n    SimpleCopyDropTarget<span style=\"border: solid 1px currentcolor;\">&lt;Derived&gt;<\/span>&gt;\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        <span style=\"border: solid 1px currentcolor;\">DropAsyncImpl<\/span>(winrt::CoreDragInfo info)\r\n    {\r\n        auto lifetime = get_strong();\r\n\r\n        auto operation = GetOperation(info);\r\n        if (!(operation &amp; winrt::DataPackageOperation::Copy)) {\r\n            co_return winrt::DataPackageOperation::None;\r\n        }\r\n\r\n        \u27e6 process the drop \u27e7\r\n\r\n        co_return winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n<\/pre>\n<p>We implement <code>DropAsync<\/code> in the base class but immediately forward the call out to the derived class&#8217;s <code>Drop\u00adAsync\u00adImpl<\/code> method. This is done via CRTP so that the derived class has full flexibility in deciding how to accept the parameters.<\/p>\n<p>If you have access to &#8220;deducing this&#8221;, then you can let the &#8220;this&#8221; deduction do the work instead of CRTP.<\/p>\n<pre>\/\/ A simple copy drop target provides no custom feedback\r\n\/\/ and accepts anything by copy.\r\nstruct SimpleCopyDropTarget :\r\n    winrt::implements&lt;SimpleCopyDropTarget,\r\n    winrt::ICoreDropOperationTarget&gt;\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        EnterAsync(winrt::CoreDragInfo const&amp; info,\r\n                   winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        OverAsync(winrt::CoreDragInfo const&amp; info,\r\n                  winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncAction\r\n        LeaveAsync(winrt::CoreDragInfo const&amp;)\r\n    {\r\n        co_return;\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    DropAsync(this auto&amp;&amp; self,                    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">              winrt::CoreDragInfo const&amp; info)     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return self.DropAsyncImpl(info);               <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                                  <\/span>\r\n\r\nprotected:\r\n    winrt::DataPackageOperation GetOperation(\r\n        winrt::CoreDragInfo const&amp; info)\r\n    {\r\n        return info.AllowedOperations() &amp;\r\n               winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n\r\nstruct Derived : winrt::implements&lt;\r\n    Derived,\r\n    SimpleCopyDropTarget&gt;\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        <span style=\"border: solid 1px currentcolor;\">DropAsyncImpl<\/span>(winrt::CoreDragInfo info)\r\n    {\r\n        auto lifetime = get_strong();\r\n\r\n        auto operation = GetOperation(info);\r\n        if (!(operation &amp; winrt::DataPackageOperation::Copy)) {\r\n            co_return winrt::DataPackageOperation::None;\r\n        }\r\n\r\n        \u27e6 process the drop \u27e7\r\n\r\n        co_return winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n<\/pre>\n<p>Another option is to weaken the &#8220;implement an interface&#8221; part of &#8220;use <code>winrt::<wbr \/>implements<\/code> to implement an interface&#8221;. We can simply omit <code>ICore\u00adDrop\u00adOperation\u00adTarget<\/code> from the list of interfaces implemented by the base class, since our base class doesn&#8217;t contain a full implementation. Instead, let the derived class finish the implementation and declare the interface there.<\/p>\n<pre>\/\/ A simple copy drop target provides no custom feedback\r\n\/\/ and accepts anything by copy.\r\nstruct SimpleCopyDropTarget :\r\n    winrt::implements&lt;SimpleCopyDropTarget,\r\n    winrt::<span style=\"border: solid 1px currentcolor;\">IInspectable<\/span>&gt; \/\/ no ICoreDropOperationTarget\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        EnterAsync(winrt::CoreDragInfo const&amp; info,\r\n                   winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        OverAsync(winrt::CoreDragInfo const&amp; info,\r\n                  winrt::CoreDragUIOverride const&amp;)\r\n    {\r\n        co_return GetOperation(info);\r\n    }\r\n\r\n    winrt::IAsyncAction\r\n        LeaveAsync(winrt::CoreDragInfo const&amp;)\r\n    {\r\n        co_return;\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">\/\/ DropAsync must be implemented by derived class<\/span>\r\n\r\nprotected:\r\n    winrt::DataPackageOperation GetOperation(\r\n        winrt::CoreDragInfo const&amp; info)\r\n    {\r\n        return info.AllowedOperations() &amp;\r\n               winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n\r\nstruct Derived : winrt::implements&lt;\r\n    Derived,\r\n    SimpleCopyDropTarget,\r\n    <span style=\"border: solid 1px currentcolor;\">winrt::ICoreDropOperationTarget<\/span>&gt;\r\n{\r\n    winrt::IAsyncOperation&lt;winrt::DataPackageOperation&gt;\r\n        <span style=\"border: solid 1px currentcolor;\">DropAsync<\/span>(winrt::CoreDragInfo info)\r\n    {\r\n        auto lifetime = get_strong();\r\n\r\n        auto operation = GetOperation(info);\r\n        if (!(operation &amp; winrt::DataPackageOperation::Copy)) {\r\n            co_return winrt::DataPackageOperation::None;\r\n        }\r\n\r\n        \u27e6 process the drop \u27e7\r\n\r\n        co_return winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n<\/pre>\n<p>Our <code>Simple\u00adCopy\u00adDrop\u00adTarget<\/code> no longer implements <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code>. Instead, it is <code>Derived<\/code> which implements <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code>. The <code>Simple\u00adCopy\u00adDrop\u00adTarget<\/code> happens to provide some really handy implementations of <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code> methods, but they aren&#8217;t actually hooked up to the <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code> interface until the <code>Derived<\/code> class says, &#8220;And I implement <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code>.&#8221;<\/p>\n<p>The upside of this is that you don&#8217;t have to use CRTP or forwarders. The downside of this is that the <code>Derived<\/code> class has to remember to say &#8220;And I implement <code>I\u00adCore\u00adDrop\u00adOperation\u00adTarget<\/code>,&#8221; because if nobody says it, then the interface isn&#8217;t implemented by anybody!<\/p>\n<p>But wait, the <code>Simple\u00adCopy\u00adDrop\u00adTarget<\/code> doesn&#8217;t implement anything interesting any more. Why are we even bothering?<\/p>\n<p>Next time, we&#8217;ll solve the problem a third way: Don&#8217;t even bother.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using CRTP to delegate the method.<\/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-110908","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Using CRTP to delegate the method.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110908","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=110908"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110908\/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=110908"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110908"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110908"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}