Last time, we finished making generalized MakeÂCompleted
and MakeÂFailed
functions for creating already-completed asynchronous activities in C++/WinRT. We left off with the observation that it’s annoying having to write out the template type parameter all the time:
winrt::Windows::Foundation::IAsyncOperationWithProgress<bool, WidgetSaveProgress> SaveChanges() { // The widget is immutable, so there are no changes to save. return MakeCompleted< winrt::Windows::Foundation::IAsyncOperationWithProgress< bool, WidgetSaveProgress>>(true); }
We can avoid the extra typing by returning a proxy object and let the conversion operator tell us what interface the caller wants.
template<typename... Result> struct async_completed { static_assert(sizeof...(Result) <= 1); std::tuple<std::decay_t<Result>...> m_result; async_completed(Result&&... result) : m_result(std::forward<Result>(result)...) {} template<typename Async> Async as() { if constexpr (sizeof...(Result) == 0) { co_return; } else { co_return std::move(std::get<0>(m_result)); } } template<typename Async> operator Async() { return as<Async>(); } }; template<typename Error> struct async_failed { std::decay_t<Error> m_error; async_failed(Error&& error) : m_error(std::forward<Error>(error)) {} template<typename Async> operator Async() { (void) co_await winrt::get_cancellation_token(); throw std::move(m_error); } };
Now you can let the proxy convert to the desired type.
winrt::Windows::Foundation::IAsyncAction SaveAsync() { return async_completed(); } winrt::Windows::Foundation::IAsyncOperation<bool> SaveAsync() { return async_completed(false); } winrt::Windows::Foundation::IAsyncAction SaveAsync() { return async_failed(winrt::hresult_access_denied()); }
Now, for cases like this, the async_completed
and async_failed
are awfully awkward-looking. The more natural way to write the methods would be
winrt::Windows::Foundation::IAsyncAction SaveAsync() { co_return; } winrt::Windows::Foundation::IAsyncOperation<bool> SaveAsync() { co_return false; } winrt::Windows::Foundation::IAsyncAction SaveAsync() { (void) co_await winrt::get_cancellation_token(); throw winrt::hresult_access_denied(); }
But sometimes you might want to create a failed or completed asynchronous activity outside of a coroutine, and for those cases, async_
and async_
might be handy.
But wait, we’re firing up the coroutine infrastructure just to return a value or throw an exception. That feels like overkill. We’ll look into a direct implementation next time.
0 comments