How can I write a C++/WinRT IAsyncOperation where T is not a Windows Runtime type?, part 2

Wait 5 sec.

Last time, we smuggled an arbitrary C++ value inside an IInspectable but noted the lack of safety. So let's add some safety.To ensure that the object is in-process, you can declare the interface as "local", meaning that the interface is for use only within a single process, and the MIDL compiler will not generate proxies for it. Even easier is just defining the interface manually, without involving the MIDL compiler at all.structDECLSPEC_UUID("⟦some guid⟧")IValueAsInspectable : ::IUnknown{};The trick here is that manually defining the interface lets us put C++ stuff inside it.template struct ValueAsInspectable;structDECLSPEC_UUID("⟦some guid⟧")IValueAsInspectable : ::IUnknown{ std::type_info const* type; template T& get_value() { if (*type != typeid(T)) { throw std::bad_cast(); } return static_cast(this) ->value; }};templatestruct ValueAsInspectable : winrt::implements{ T value; template ValueAsInspectable(Args&&... args) : value{ std::forward(args)... } { this->>type = std::addressof(typeid(T)); }};The IValueAsInspectable has a COM interface ID, so it can be queried for. But once you get it, you can call the C++ method get_value(). That method first validates that you are using the matching T, throwing a bad_cast exception if not. If you pass that test, then it returns a reference to the value hiding inside.And for convenience, we can add these helpers. The first two we have seen already:templatewinrt::Windows::Foundation::IInspectable MakeValueAsInspectable(Args&&... args){ return winrt::make( std::forward(args)...);}templatewinrt::Windows::Foundation::IInspectable MakeValueAsInspectable(T&& arg){ return winrt::make>( std::forward(arg));}Next are functions for extracting the value from an object that we believe to be a Value­As­Inspectable.templateT& ValueRefFromInspectable( winrt::Windows::Foundation::IInspectable const& arg){ return arg.as()-> get_value();}templateT CopyValueFromInspectable( winrt::Windows::Foundation::IInspectable const& arg){ return ValueRefFromInspectable(arg);}templateT MoveValueFromInspectable( winrt::Windows::Foundation::IInspectable && arg){ return std::move(ValueRefFromInspectable(arg));}The basic function is Value­Ref­From­Inspectable which produces an lvalue reference from an inspectable that we assume represents a Value­As­Inspectable. (If we're wrong, it throws a bad_cast exception.) Building on that are two functions which either copy or move the value out of the Value­As­Inspectable.I gave the function that returns an lvalue name that emphasizes that you get a reference from the inspectable, hoping to prevent people from getting a reference to an expiring inspectable:// Code in italics is wrong// Don't do this.auto& value = ValueRefFromInspectablt( co_await DoSomethingAsync());Note also that if you modify the value reference or move the value out of the inpectable, this affects the underlying object, which means that other people that have a reference to the same inspectable will see the object change state.void MutateTheWidget(winrt::Windows::Foundation::IInspectable obj){ auto& widget = ValueRefFromInspectable(obj); ⟦ do something that modifies the widget ⟧}winrt::fire_and_forget Sample(){ auto obj = co_await DoSomethingAsync(); MutateTheWidget(obj); auto& widget = ValueRefFromInspectable(obj); // this code sees that the widget has been mutated}But really, if you need a coroutine that produces a non-Windows Runtime type, then use a non-Windows Runtime coroutine library. I'm personally partial to wil::task (and its COM-aware buddy wil::com_task), seeing as I wrote it.