{"id":107732,"date":"2023-01-20T07:00:21","date_gmt":"2023-01-20T15:00:21","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107732"},"modified":"2023-01-21T21:40:40","modified_gmt":"2023-01-22T05:40:40","slug":"how-can-i-call-a-method-on-a-derived-class-from-a-base-class-say-to-get-a-strong-reference-to-the-containing-object","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230120-21\/?p=107732","title":{"rendered":"How can I call a method on a derived class from a base class, say, to get a strong reference to the containing object?"},"content":{"rendered":"<p>Suppose you have a base class, and you want to get a strong reference to your derived class. You may need to do this, for example, if your base class has a method which is a coroutine, and it needs to retain a strong reference to itself so that it can extend its lifetime into the coroutine body, thereby preventing the object from being destroyed after the coroutine reaches its first suspension point.<\/p>\n<pre>\/\/ C++\/WinRT style\r\n\r\nstruct Base\r\n{\r\n    winrt::IAsyncAction DoSomethingAsync()\r\n    {\r\n        auto lifetime = get_strong(); \/\/ ????\r\n\r\n        co_await this-&gt;step1();\r\n\r\n        this-&gt;step2();\r\n    }\r\n};\r\n\r\nstruct Derived : DerivedT&lt;Derived&gt;, Base\r\n{\r\n};\r\n\r\n\/\/ PPL style\r\n\r\nstruct Base\r\n{\r\n    Concurrency::task&lt;void&gt; DoSomethingAsync()\r\n    {\r\n        auto lifetime = shared_from_this(); \/\/ ????\r\n\r\n        co_await this-&gt;step1();\r\n\r\n        this-&gt;step2();\r\n    }\r\n};\r\n\r\nstruct Derived : std::enable_shared_from_this&lt;Derived&gt;, Base\r\n{\r\n};\r\n<\/pre>\n<p>First, let&#8217;s forget about coroutines. They are the motivation for the question, but they aren&#8217;t really relevant to the solution.<\/p>\n<p>If you have an instance of a base class and you want to get an instance of the derived class, what can you do?<\/p>\n<p>Well, if you know what the derived class is, you can downcast to it.<\/p>\n<pre>\/\/ C++\/WinRT style\r\nstruct Base\r\n{\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">static_cast&lt;Derived*&gt;(this)-&gt;<\/span>get_strong();\r\n\r\n        ...\r\n    }\r\n};\r\n\r\n\/\/ PPL style\r\n\r\nstruct Base\r\n{\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">static_cast&lt;Derived*&gt;(this)-&gt;<\/span>shared_from_this();\r\n\r\n        ...\r\n    }\r\n};\r\n<\/pre>\n<p>This trick works only if you are absolutely certain that the <code>Base<\/code> is the base portion of a <code>Derived<\/code>. Now, maybe you want to use the <code>Base<\/code> as the base of multiple derived classes. In that case, you can pass the derived class as a template parameter, turning this into a case of CRTP:<\/p>\n<pre>\/\/ C++\/WinRT style\r\n\r\n<span style=\"color: #08f;\">template&lt;typename D&gt;<\/span>\r\nstruct Base\r\n{\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">static_cast&lt;D*&gt;(this)-&gt;<\/span>get_strong();\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : DerivedT&lt;Derived&gt;, <span style=\"color: #08f;\">Base&lt;Derived&gt;<\/span>\r\n{\r\n};\r\n\r\n\/\/ PPL style\r\n\r\n<span style=\"color: #08f;\">template&lt;typename D&gt;<\/span>\r\nstruct Base\r\n{\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">static_cast&lt;D*&gt;(this)-&gt;<\/span>shared_from_this();\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : std::enable_shared_from_this&lt;Derived&gt;, <span style=\"color: #08f;\">Base&lt;Derived&gt;<\/span>\r\n{\r\n};\r\n<\/pre>\n<p>Another solution is to give the <code>Base<\/code> a weak reference to the container. This removes the need to know what the container is.<\/p>\n<pre>\/\/ C++\/WinRT style\r\n\r\nstruct Base\r\n{\r\n    winrt::weak_ptr&lt;IInspectable&gt; derived_weak;\r\n\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">derived_weak.get()<\/span>;\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : DerivedT&lt;Derived&gt;, Base\r\n{\r\n    Derived()\r\n    {\r\n        <span style=\"color: #08f;\">derived_weak = get_weak();<\/span>\r\n    }\r\n};\r\n\r\n\/\/ PPL style\r\n\r\n<span style=\"color: #08f;\">template&lt;typename D&gt;<\/span>\r\nstruct Base\r\n{\r\n    std::weak_ptr&lt;void&gt; derived_weak;\r\n\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">derived_weak.lock()<\/span>;\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : std::enable_shared_from_this&lt;Derived&gt;, Base\r\n{\r\n    Derived()\r\n    {\r\n        \/\/ doesn't work!\r\n    }\r\n};\r\n<\/pre>\n<p>Uh-oh, we&#8217;re kind of stuck in the PPL case because the weak pointer hiding inside <code>enable_<wbr>shared_<wbr>from_<wbr>this<\/code> is not initialized at the point the <code>Derived<\/code> is constructed. (It gets set by <code>std::make_<wbr>shared<\/code> after the object has been constructed.)<\/p>\n<p>Another option is to have a pure virtual method which derived classes must implement in order to provide the necessary strong pointer.<\/p>\n<pre>\/\/ C++\/WinRT style\r\n\r\nstruct Base\r\n{\r\n    virtual winrt::IInspectable derived_strong() const = 0;\r\n\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = derived_strong();\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : DerivedT&lt;Derived&gt;, Base\r\n{\r\n    winrt::IInspectable derived_strong() const override\r\n    {\r\n        return *this;\r\n    }\r\n};\r\n\r\n\/\/ PPL style\r\n\r\nstruct Base\r\n{\r\n    virtual std::shared_ptr&lt;const void&gt; derived_strong() const = 0;\r\n\r\n    void BaseMethod()\r\n    {\r\n        auto lifetime = derived_strong();\r\n\r\n        ...\r\n    }\r\n};\r\n\r\nstruct Derived : std::enable_shared_from_this&lt;Derived&gt;, Base\r\n{\r\n    std::shared_ptr&lt;const void&gt; derived_strong() const override\r\n    {\r\n        return shared_from_this();\r\n    }\r\n};\r\n<\/pre>\n<p>And C++23&#8217;s &#8220;deducing this&#8221; adds another option:<\/p>\n<pre>\/\/ C++\/WinRT style\r\n\r\nstruct Base\r\n{\r\n    <span style=\"color: #08f;\">template&lt;typename Derived&gt;<\/span>\r\n    winrt::IAsyncAction DoSomethingAsync(<span style=\"color: #08f;\">this Derived&amp;&amp; self<\/span>)\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">self.<\/span>get_strong();\r\n\r\n        co_await this-&gt;step1();\r\n\r\n        this-&gt;step2();\r\n    }\r\n}\r\n\r\nstruct Derived : DerivedT&lt;Derived&gt;, Base\r\n{\r\n};\r\n\r\n\/\/ PPL style\r\n\r\nstruct Base\r\n{\r\n    <span style=\"color: #08f;\">template&lt;typename Derived&gt;<\/span>\r\n    Concurrency::task&lt;void&gt; DoSomethingAsync(<span style=\"color: #08f;\">this Derived&amp;&amp; self<\/span>)\r\n    {\r\n        auto lifetime = <span style=\"color: #08f;\">self.<\/span>shared_from_this();\r\n\r\n        co_await this-&gt;step1();\r\n\r\n        this-&gt;step2();\r\n    }\r\n};\r\n\r\nstruct Derived : std::enable_shared_from_this&lt;Derived&gt;, Base\r\n{\r\n};\r\n<\/pre>\n<p>Which should you choose?<\/p>\n<p>Well, it&#8217;s up to you. My preference is to use the CRTP pattern for C++\/WinRT types, since that avoids extra data members and vtables, and it is consistent with other C++\/WinRT patterns. And that leads me to prefer the CRTP pattern for the PPL case, too, for consistency.<\/p>\n<p>And if you can assume C++23, then the &#8220;deducing this&#8221; form lets you get the benefits of CRTP without having to do CRTP. So that&#8217;s my first choice.<\/p>\n<p>But that&#8217;s just my opinion. You might prefer something else.<\/p>\n<p><b>Bonus chatter<\/b>: You might think of having <code>Base<\/code> derive from <code>enable_<wbr>shared_<wbr>from_<wbr>this<\/code>. That would work, but it also means that <code>Derived<\/code> cannot derive from <code>enable_<wbr>shared_<wbr>from_<wbr>this<\/code>, and it also means that <code>Derived<\/code> cannot derive from both <code>Base1<\/code> and <code>Base2<\/code> if both of the base classes derive from <code>enable_<wbr>shared_<wbr>from_<wbr>this<\/code>. The general convention is that the most derived class is the one that gets to derive from <code>enable_<wbr>shared_<wbr>from_<wbr>this<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A variety of patterns are available.<\/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":[],"class_list":["post-107732","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing"],"acf":[],"blog_post_summary":"<p>A variety of patterns are available.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107732","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=107732"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107732\/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=107732"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107732"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107732"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}