{"id":110905,"date":"2025-02-25T07:00:00","date_gmt":"2025-02-25T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110905"},"modified":"2025-02-25T07:29:45","modified_gmt":"2025-02-25T15:29:45","slug":"20250225-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250225-00\/?p=110905","title":{"rendered":"C++\/WinRT implementation inheritance: Notes on <CODE>winrt::implements<\/CODE>, part 5"},"content":{"rendered":"<p>Last time, <a title=\"C++\/WinRT implementation inheritance: Notes on winrt::implements, part 4\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250224-00\/?p=110901\"> we saw how you can derive from <code>winrt::<wbr \/>implements<\/code> to form a new base class<\/a>, in the case that the new base class is self-contained.<\/p>\n<p>But what if the new base class isn&#8217;t self-contained? For example, maybe you want the base class to do the boring paperwork and defer to the derived class for the actual work.<\/p>\n<pre>namespace winrt\r\n{\r\n    using namespace winrt::Windows::Foundation;\r\n    using namespace winrt::Windows::Web::Http;\r\n    using namespace winrt::Windows::Web::Http::Filters;\r\n}\r\n\r\nstruct ObserverFilter :\r\n    winrt::implements&lt;ObserverFilter, winrt::IHttpFilter&gt;\r\n{\r\n    ObserverFilter(winrt::IHttpFilter const&amp; downstream) :\r\n        m_downstream(downstream) {}\r\n\r\n    winrt::IAsyncActionWithProgress&lt;\r\n        winrt::HttpResponseMessage, winrt::HttpProgress&gt;\r\n        SendRequestAsync(winrt::HttpRequestMessage const&amp; message)\r\n    {\r\n        \u27e6 let derived class observe the message \u27e7\r\n        return m_downstream.SendRequestAsync(message);\r\n    }\r\nprivate:\r\n    winrt::IHttpFilter m_downstream;\r\n};\r\n<\/pre>\n<p>This <code>Observer\u00adFilter<\/code> base class does the grunt work of being an HTTP filter (managing the downstream), with a place for the derived class to plug in and do the observing.<\/p>\n<p>You can solve this problem by simply setting aside that you&#8217;re working in C++\/WinRT and solving it using standard C++. In standard C++, you would use a pure virtual method.<\/p>\n<pre>struct ObserverFilter :\r\n    winrt::implements&lt;ObserverFilter, winrt::IHttpFilter&gt;\r\n{\r\n    ObserverFilter(winrt::IHttpFilter const&amp; downstream) :\r\n        m_downstream(downstream) {}\r\n\r\n    winrt::IAsyncActionWithProgress&lt;\r\n        winrt::HttpResponseMessage, winrt::HttpProgress&gt;\r\n        SendRequestAsync(winrt::HttpRequestMessage const&amp; message)\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">ObserveMessage(message);<\/span>\r\n        return m_downstream.SendRequestAsync(message);\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">virtual void ObserveMessage(                      <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    winrt::HttpRequestMessage const&amp; message) = 0;<\/span>\r\nprivate:\r\n    winrt::IHttpFilter m_downstream;\r\n};\r\n<\/pre>\n<p>The derived class would then implement the <code>Observe\u00adMessage<\/code> method.<\/p>\n<pre>struct Derived : winrt::implements&lt;Derived, ObserverFilter&gt;\r\n{\r\n    virtual void ObserveMessage(\r\n        winrt::HttpRequestMessage const&amp; message) override\r\n    {\r\n        \u27e6 observe the message \u27e7\r\n    }\r\n};\r\n<\/pre>\n<p>If the helper base class wants to leave one of the interface methods completely to the derived class, you still have to define it in the base class. You can&#8217;t just leave it blank and say &#8220;Don&#8217;t worry, my derived class will take care of it.&#8221;<\/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    \/\/ I want to let the derived class implement DropAsync\r\n\r\nprivate:\r\n    winrt::DataPackageOperation GetOperation(\r\n        winrt::CoreDragInfo const&amp; info)\r\n    {\r\n        return info.AllowedOperations() &amp;&amp;\r\n               winrt::DataPackageOperation::Copy;\r\n    }\r\n};\r\n<\/pre>\n<p>This doesn&#8217;t work because C++\/WinRT expects that if you declare that you implement an interface (in this case, <code>ICore\u00adDrop\u00adOperation\u00adTarget<\/code>), then you implement all of its methods. It requires this so that it can generate the <code>ICore\u00adDrop\u00adOperation\u00adTarget<\/code> vtable entries.<\/p>\n<p>One solution is to declare the method as pure virtual, saying, &#8220;Trust me, the derived class will do it.&#8221;<\/p>\n<pre>struct SimpleCopyDropTarget :\r\n    winrt::implements&lt;SimpleCopyDropTarget,\r\n    winrt::ICoreDropOperationTarget&gt;\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">virtual winrt::IAsyncOperation&lt;DataPackageOperation&gt;<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    DropAsync(winrt::CoreDragInfo const&amp; info) = 0; <\/span>\r\n\r\nprivate:\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>This does have the downside of requiring the derived class to accept the parameters exactly as you specified, even if they might prefer a different form. For example, since <code>Over\u00adAsync<\/code> is a coroutine, the derived class might prefer to accept the <code>info<\/code> by value so that it can carry it across a suspension point.<\/p>\n<p>There are many solutions to this problem. We&#8217;ll look at one of the next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When the base class isn&#8217;t self-contained.<\/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-110905","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When the base class isn&#8217;t self-contained.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110905","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=110905"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110905\/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=110905"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110905"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110905"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}