{"id":109977,"date":"2024-07-18T07:00:00","date_gmt":"2024-07-18T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109977"},"modified":"2024-06-24T11:02:37","modified_gmt":"2024-06-24T18:02:37","slug":"20240718-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240718-00\/?p=109977","title":{"rendered":"Creating an already-completed asynchronous activity in C++\/WinRT, part 8"},"content":{"rendered":"<p>Last time, we <a title=\"Creating an already-completed asynchronous activity in C++\/WinRT, part 7\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240717-00\/?p=109975\"> created an already-completed <code>IAsyncAction<\/code> by implementing interface directly<\/a>. Now we&#8217;ll generalize this cover all four Windows Runtime asynchronous activities.<\/p>\n<p>We start with a traits class that tells us the various pieces that go into each Windows Runtime asynchronous interface.<\/p>\n<pre>template&lt;typename Async&gt;\r\nstruct winrt_async_traits;\r\n\r\ntemplate&lt;&gt;\r\nstruct winrt_async_traits&lt;\r\n    winrt::Windows::Foundation::IAsyncAction&gt;\r\n{\r\n    using Async = winrt::Windows::Foundation::IAsyncAction;\r\n    static constexpr bool has_progress = false;\r\n    using Progress = int32_t;\r\n    using Result = std::monostate;\r\n    using CompletedHandler =\r\n        winrt::Windows::Foundation::AsyncActionCompletedHandler;\r\n    using ProgressHandler = winrt::IInspectable;\r\n};\r\n\r\ntemplate&lt;typename P&gt;\r\nstruct winrt_async_traits&lt;\r\n    winrt::Windows::Foundation::IAsyncActionWithProgress&lt;P&gt;&gt;\r\n{\r\n    using Async = winrt::Windows::Foundation::IAsyncActionWithProgress&lt;P&gt;;\r\n    static constexpr bool has_progress = true;\r\n    using Progress = P;\r\n    using Result = std::monostate;\r\n    using CompletedHandler =\r\n        winrt::Windows::Foundation::AsyncActionWithProgressCompletedHandler&lt;P&gt;;\r\n    using ProgressHandler =\r\n        winrt::Windows::Foundation::AsyncActionProgressHandler&lt;P&gt;;\r\n};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct winrt_async_traits&lt;\r\n    winrt::Windows::Foundation::IAsyncOperation&lt;T&gt;&gt;\r\n{\r\n    using Async = winrt::Windows::Foundation::IAsyncOperation&lt;T&gt;;\r\n    static constexpr bool has_progress = false;\r\n    using Progress = int32_t;\r\n    using Result = T;\r\n    using CompletedHandler =\r\n        winrt::Windows::Foundation::AsyncOperationCompletedHandler&lt;T&gt;;\r\n    using ProgressHandler = winrt::IInspectable;\r\n};\r\n\r\ntemplate&lt;typename T, typename P&gt;\r\nstruct winrt_async_traits&lt;\r\n    winrt::Windows::Foundation::IAsyncOperationWithProgress&lt;T, P&gt;&gt;\r\n{\r\n    using Async = winrt::Windows::Foundation::IAsyncOperationWithProgress&lt;T, P&gt;;\r\n    static constexpr bool has_progress = true;\r\n    using Progress = P;\r\n    using Result = T;\r\n    using CompletedHandler =\r\n        winrt::Windows::Foundation::AsyncOperationWithProgressCompletedHandler&lt;T, P&gt;;\r\n    using ProgressHandler =\r\n        winrt::Windows::Foundation::AsyncOperationProgressHandler&lt;T, P&gt;;\r\n};\r\n<\/pre>\n<p>I use <code>monostate<\/code> as placeholders for the cases where the interface doesn&#8217;t have a result or a progress handler. I could have used <code>void<\/code>, but I&#8217;m picking an instantiatable type so I can use it as the type of member variables and parameters below. I could have tried to compress them out, but they aren&#8217;t going to be large, and it allows for COMDAT folding of the various specializations since all the members lie at the same offsets.<\/p>\n<pre>template&lt;typename D, typename Async&gt;\r\nstruct completed_async_base\r\n{\r\n    using Traits = winrt_async_traits&lt;Async&gt;;\r\n    using CompletedHandler = typename Traits::CompletedHandler;\r\n    using ProgressHandler = typename Traits::ProgressHandler;\r\n\r\n    auto outer() { return static_cast&lt;D*&gt;(this); }\r\n\r\n    winrt::slim_mutex m_mutex;\r\n    CompletedHandler m_completed;\r\n    ProgressHandler m_progress;\r\n\r\n    auto Id() { return 1; }\r\n    \/\/ Status and Error code implemented by D\r\n    auto Cancel() { }\r\n    auto Close() { }\r\n\r\n    auto Completed()\r\n    {\r\n        winrt::slim_lock_guard lock(m_mutex);\r\n        return m_completed;\r\n    }\r\n\r\n    auto Completed(CompletedHandler const&amp; handler)\r\n    {\r\n        {\r\n            winrt::slim_lock_guard lock(m_mutex);\r\n            if (m_completed) {\r\n                throw winrt::hresult_illegal_delegate_assignment();\r\n            }\r\n            m_completed = handler;\r\n        }\r\n        handler(*outer(), outer()-&gt;Status());\r\n    }\r\n\r\n    auto Progress()\r\n    {\r\n        winrt::slim_lock_guard lock(m_mutex);\r\n        return m_progress;\r\n    }\r\n\r\n    auto Progress(ProgressHandler const&amp; handler)\r\n    {\r\n        winrt::slim_lock_guard lock(m_mutex);\r\n        m_progress = handler;\r\n    }\r\n};\r\n<\/pre>\n<p>The <code>completed_<wbr \/>async_<wbr \/>base<\/code> takes care of most of the paperwork of being a Windows Runtime asynchronous interface. We defer the status reporting to our derived class because we&#8217;re going to use this both for successful and failed asynchronous activities. Some of the interfaces don&#8217;t have a <code>Progress<\/code> property, in which case our implementations here will never be instantiated.<\/p>\n<pre>template&lt;typename Async&gt;\r\nstruct completed_async :\r\n    winrt::implements&lt;completed_async&lt;Async&gt;,\r\n        Async,\r\n        winrt::Windows::Foundation::IAsyncInfo&gt;,\r\n    completed_async_base&lt;completed_async&lt;Async&gt;, Async&gt;\r\n{\r\n    using Traits = typename completed_async::Traits;\r\n    using Result = typename Traits::Result;\r\n\r\n    completed_async() = default;\r\n    completed_async(Result const&amp; value) : m_result(value) {}\r\n\r\n    Result m_result = <a title=\"Producing an empty Windows Runtime type in C++\/WinRT\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220504-00\/?p=106569\">winrt_empty_value()<\/a>;\r\n\r\n    auto Status() { return winrt::Windows::Foundation::AsyncStatus::Completed; }\r\n    auto ErrorCode() { return S_OK; }\r\n\r\n    auto GetResults() {\r\n        if constexpr (Traits::has_progress) {\r\n            return m_result;\r\n        } else {\r\n            winrt::slim_lock_guard lock(m_mutex);\r\n            return std::move(m_result);\r\n        }\r\n    }\r\n};\r\n<\/pre>\n<p>For a successfully-completed asynchronous activity, we return a status of <code>Completed<\/code>, an error code of <code>S_OK<\/code>, and a result consisting of the value we were given at construction.<\/p>\n<p>In the case of an <code>IAsync\u00adAction<\/code> or <code>IAsync\u00adAction\u00adWith\u00adProgress<\/code>, there is no <code>Result<\/code>, but we have a dummy <code>monostate<\/code> variable to save myself a bunch of annoying typing. I could have used <code>bool<\/code>, but I chose <code>monostate<\/code> because it is not a legal template argument to <code>IAsyncOperation<\/code>, so you can&#8217;t accidentally synthesize a successful <code>IAsyncOperation<\/code> without also providing a value. Thanks to the magic of CRTP, the <code>monostate<\/code> returned by <code>GetResults()<\/code> is discarded by the projection.<\/p>\n<p>It is legal to get the results of a <code>-With\u00adProgress<\/code> interface multiple times, so we return a copy for progress interfaces, but move the result out if it is a one-shot.<\/p>\n<p>But wait, moving the value out requires us to take a lock, so that we don&#8217;t have the case that two threads both try to move the value out at the same time. We can avoid the mutex by always returning a copy.<\/p>\n<pre>    auto GetResults() {\r\n        <span style=\"border: solid 1px currentcolor;\">return m_result;<\/span>\r\n    }\r\n<\/pre>\n<p>Whether this is a net benefit depends on the relative cost between a mutex and a copy of the result type.<\/p>\n<p>The other half of the story is the failed activity.<\/p>\n<pre>template&lt;typename Async&gt;\r\nstruct failed_async :\r\n    winrt::implements&lt;failed_async,\r\n        Async,\r\n        winrt::Windows::Foundation::IAsyncInfo&gt;,\r\n    completed_async_base&lt;failed_async&lt;Async&gt;, Async&gt;\r\n{\r\n    using Traits = typename completed_async::Traits;\r\n\r\n    failed_async(std::exception_ptr const&amp; exception) :\r\n        m_exception(exception) {}\r\n\r\n    std::exception_ptr m_exception;\r\n\r\n    auto Status() { return AsyncStatus::Error; }\r\n    auto ErrorCode() {\r\n        try { GetResults(); }\r\n        catch (...) { return winrt::to_hresult(); }\r\n    }\r\n\r\n    [[noreturn]] void GetResults() {\r\n        std::rethrow_exception(m_exception);\r\n    }\r\n};\r\n<\/pre>\n<p>The failed variant is simpler. All it needs to remember is an <code>exception_ptr<\/code>, which it uses to extract the error code, and which it rethrows when the results are requested.<\/p>\n<p>We can use our earlier trick of holding the information in a proxy object and producing the interface by conversion. Rename <code>completed_<wbr \/>async<\/code> and <code>failed_<wbr \/>async<\/code> to <code>completed_<wbr \/>async_<wbr \/>impl<\/code> and <code>failed_<wbr \/>async_<wbr \/>impl<\/code>, respectively, and define these proxy classes:<\/p>\n<pre>template&lt;typename...Result&gt;\r\nstruct completed_async\r\n{\r\n    static_assert(sizeof...(Result) &lt;= 1);\r\n    std::tuple&lt;std::decay_t&lt;Result&gt;...&gt; m_result;\r\n\r\n    completed_async(Result&amp;&amp;... result) :\r\n        m_result(std::forward&lt;Result&gt;(result)...) {}\r\n\r\n    template&lt;typename Async&gt;\r\n    Async <a title=\"It's great that you provide operator overloads, but it's also nice to have names\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230605-00\/?p=108289\">as<\/a>() {\r\n        return std::make_from_tuple&lt;\r\n            completed_async_impl&lt;Async&gt;(\r\n                std::move(m_result));\r\n    }\r\n\r\n    template&lt;typename Async&gt;\r\n    operator Async() { return as&lt;Async&gt;(); }\r\n};\r\n\r\ntemplate&lt;typename Error&gt;\r\nstruct failed_async\r\n{\r\n    std::exception_ptr m_exception;\r\n\r\n    failed_async(std::exception_ptr const&amp; exception) :\r\n        m_exception(exception) {}\r\n\r\n    template&lt;typename Error&gt;\r\n    failed_async(Error&amp;&amp; error) :\r\n        m_exception(\r\n            std::make_exception_ptr(\r\n                std::forward&lt;Error&gt;(Error))) {}\r\n\r\n    template&lt;typename Async&gt;\r\n    Async as() {\r\n        return failed_async_impl&lt;Async&gt;(\r\n            m_exception);\r\n    }\r\n\r\n    template&lt;typename Async&gt;\r\n    operator Async() { return as&lt;Async&gt;(); }\r\n};\r\n<\/pre>\n<p>Next time, we&#8217;ll cheat a little bit.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Generalizing the pattern.<\/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-109977","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Generalizing the pattern.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109977","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=109977"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109977\/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=109977"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109977"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109977"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}