{"id":111691,"date":"2025-10-16T07:00:00","date_gmt":"2025-10-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111691"},"modified":"2025-10-16T09:06:07","modified_gmt":"2025-10-16T16:06:07","slug":"20251016-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251016-00\/?p=111691","title":{"rendered":"Using RAII to remedy a defect where not all code paths performed required exit actions"},"content":{"rendered":"<p>A team asked me to review their pull request that fixed a bug that was caused by failing to perform some required action along all code paths. Here&#8217;s a simplified sketch:<\/p>\n<pre>void MySpecialFeature::OnButtonClick()\r\n{\r\n    try {\r\n        auto file = PickFile();\r\n        if (!file) {\r\n            DismissUI();\r\n            return;\r\n        }\r\n\r\n        if (ConfirmAction()) {\r\n            if (m_useAlgorithm1) {\r\n                \/\/ StartAlgorithm1 invokes the lambda when finished.\r\n                StartAlgorithm1(file, [self = shared_from_this()] {\r\n                    self-&gt;DismissUI();\r\n                });\r\n            } else {\r\n                RunAlgorithm2(file);\r\n                DismissUI();\r\n            }\r\n        } <span style=\"border: solid 1px currentcolor; border-bottom: none;\">else { \/\/ this block was missing<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: solid none none solid;\">\u00a0 <\/span><span style=\"border-right: solid 1px currentcolor;\">  DismissUI();                  <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                 <\/span>\r\n    } catch (...) {\r\n        DismissUI();\r\n    }\r\n}\r\n<\/pre>\n<p>The problem was the <code>DismissUI()<\/code> call was missing on one of the code paths.<\/p>\n<p>I suggested that they simplify the code by using an RAII type which will dismiss the UI on all code paths automatically.<\/p>\n<p>The <a href=\"http:\/\/github.com\/microsoft\/wil\"> Windows Implementation Library<\/a> (commonly known as wil) provides a helper called <code>scope_exit<\/code> which creates and returns an RAII object whose destructor runs the lambda you specify.\u00b9 We can tell it to run a lambda that calls <code>DismissUI()<\/code>.<\/p>\n<p>Here&#8217;s the first try:<\/p>\n<pre>void MySpecialFeature::OnButtonClick()\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">auto ensureDismiss = wil::scope_exit([&amp;] { DismissUI(); });<\/span>\r\n    try {\r\n        auto file = PickFile();\r\n        if (!file) {\r\n            <span style=\"border: dashed 1px currentcolor;\">\/\/ <span style=\"text-decoration: line-through;\">DismissUI();<\/span><\/span>\r\n            return;\r\n        }\r\n\r\n        if (ConfirmAction()) {\r\n            if (m_useAlgorithm1) {\r\n                \/\/ StartAlgorithm1 invokes the lambda when finished.\r\n                StartAlgorithm1(file, [self = shared_from_this()] {\r\n                    <span style=\"border: solid 1px currentcolor;\">???<\/span>\r\n                });\r\n            } else {\r\n                RunAlgorithm2(file);\r\n                <span style=\"border: dashed 1px currentcolor;\">\/\/ <span style=\"text-decoration: line-through;\">DismissUI();<\/span><\/span>\r\n            }\r\n        } <span style=\"border: dashed 1px currentcolor; border-bottom: none;\">\/\/ <span style=\"text-decoration: line-through;\">else {<\/span>      <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: dashed none none dashed;\">\u00a0 <\/span><span style=\"border-right: dashed 1px currentcolor;\">\/\/ <span style=\"text-decoration: line-through;\">DismissUI();<\/span><\/span>\r\n        <span style=\"border: dashed 1px currentcolor; border-top: none;\">\/\/ <span style=\"text-decoration: line-through;\">}<\/span>             <\/span>\r\n    } catch (...) {\r\n        <span style=\"border: dashed 1px currentcolor;\">\/\/ <span style=\"text-decoration: line-through;\">DismissUI();<\/span><\/span>\r\n    }\r\n}\r\n<\/pre>\n<p>The <code>ensureDismiss<\/code> is an RAII type created by <code>scope_exit<\/code> that runs the lambda at destruction, which in this case means that it calls <code>DismissUI()<\/code>. The nice thing about C++ destruction of local variables is that it happens when scope ends, whether it be by executing off the end of the block, by an early exit (say, an early <code>return<\/code>), or an exception.<\/p>\n<p>There is one section marked with question marks, though. We don&#8217;t want <code>DismissUI()<\/code> to happen until Algorithm1 finishes its work. How do we delay the destruction of the <code>ensureDismiss<\/code> object?<\/p>\n<p>Well, we can&#8217;t. It will destruct when the scope ends.<\/p>\n<p>However, the objects created by <code>wil::<wbr \/>scope_exit<\/code> are movable. When you move them, the obligation to run the lambda is transferred to the moved-to object. So you can move-capture the <code>ensureDismiss<\/code> into the lambda, and that will transfer the responsibility to call <code>DismissUI()<\/code> into the lambda. Fortunately, the lambda already captures <code>shared_from_this()<\/code>, so there is a <code>shared_ptr&lt;MySpecialFeature&gt;<\/code> inside the lambda. We can&#8217;t see it, but it&#8217;s clear that <code>StartAlgorithm1<\/code> must move its lambda to some safekeeping location so that it can invoke it later. When the lambda moves, the obligation moves with it, and when the final moved-to lambda destructs, the <code>DismissUI()<\/code> will happen.<\/p>\n<pre>void MySpecialFeature::OnButtonClick()\r\n{\r\n    auto ensureDismiss = wil::scope_exit([<span style=\"border: solid 1px currentcolor;\">=<\/span>] { DismissUI(); });\r\n    try {\r\n        auto folder = PickOutputFolder();\r\n        if (!folder) {\r\n            return;\r\n        }\r\n\r\n        if (ConfirmAction()) {\r\n            if (m_useAlgorithm1) {\r\n                \/\/ StartAlgorithm1 invokes the lambda when finished.\r\n                StartAlgorithm1(file, [self = shared_from_this(),\r\n                    <span style=\"border: solid 1px currentcolor;\">ensureDismiss = std::move(ensureDismiss)<\/span>]\r\n                    { });\r\n            } else {\r\n                RunAlgorithm2(file);\r\n            }\r\n        }\r\n    } catch (...) {\r\n    }\r\n}\r\n<\/pre>\n<p>Note that we changed the capture of the lambda passed to <code>scope_exit<\/code> a value capture, because a reference capture would be capturing references to locals that have destructed. In this case, we don&#8217;t capture any locals, so it&#8217;s not significant, but it&#8217;s good hygiene and avoids embarrasing questions later. (A hidden gotcha is that a value capture captures <code>this<\/code> by value, so there is actually still a reference capture in there, namely a reference to <code>*this<\/code>.)<\/p>\n<p>In fact, for even better hygiene, you might want to capture the <code>shared_ptr<\/code> into the lambda so that the lifetime promises are more explicit.<\/p>\n<pre>void MySpecialFeature::OnButtonClick()\r\n{\r\n    auto ensureDismiss = wil::scope_exit([<span style=\"border: solid 1px currentcolor;\">self = shared_from_this()<\/span>]\r\n            { self-&gt;DismissUI(); });\r\n    try {\r\n        auto folder = PickOutputFolder();\r\n        if (!folder) {\r\n            return;\r\n        }\r\n\r\n        if (ConfirmAction()) {\r\n            if (m_useAlgorithm1) {\r\n                \/\/ StartAlgorithm1 invokes the lambda when finished.\r\n                StartAlgorithm1(file, [<span style=\"border: dashed 1px currentcolor;\">\/\/ <span style=\"text-decoration: line-through;\">self = shared_from_this(),<\/span><\/span>\r\n                    <span style=\"border: solid 1px currentcolor;\">ensureDismiss = std::move(ensureDismiss)<\/span>]\r\n                    { });\r\n            } else {\r\n                RunAlgorithm2(file);\r\n            }\r\n        }\r\n    } catch (...) {\r\n    }\r\n}\r\n<\/pre>\n<p>There&#8217;s a little wrinkle here that we can iron out look at next time.<\/p>\n<p><b>Bonus chatter<\/b>: The object created by <code>wil::<wbr \/>scope_exit<\/code> patterns itself after <code>std::unique_ptr<\/code>, so you can <code>reset()<\/code> it to go through its destructor cleanup early, and you can <code>release()<\/code> it to say &#8220;I changed my mind. Don&#8217;t run the lambda at all.&#8221;<\/p>\n<p>\u00b9 A corresponding proposal for the C++ standard library has been around <a title=\"A Proposal to Add additional RAII Wrappers to the Standard Library\" href=\"http:\/\/wg21.link\/n3677\"> since 2013<\/a>. (Followed up by <a href=\"https:\/\/wg21.link\/n3830\">N3830<\/a>, <a href=\"https:\/\/wg21.link\/n3949\">N3949<\/a>, <a href=\"https:\/\/wg21.link\/n4189\">N4189<\/a>, <a href=\"https:\/\/wg21.link\/p0052r1\">p0052r1<\/a>, through <a href=\"https:\/\/wg21.link\/p0052r10\">p0052r10<\/a>, then became part of <a href=\"https:\/\/wg21.link\/n4786\">N4786<\/a> (Working Draft, C++ Extensions for Library Fundamentals, Version 3), which appears to have been approved in 2024 as an experimental feature.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Passing the obligation onward.<\/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-111691","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing"],"acf":[],"blog_post_summary":"<p>Passing the obligation onward.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111691","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=111691"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111691\/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=111691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}