Cody
Cody

Reputation: 639

Does `wil::com_ptr` overload operator &, aka "address of"?

I found this code snippet here.

wil::com_ptr<IStream> stream;
CHECK_FAILURE(SHCreateStreamOnFileEx(
    L"assets/EdgeWebView2-80.jpg", STGM_READ, FILE_ATTRIBUTE_NORMAL,
    FALSE, nullptr, &stream));

According to the manual of SHCreateStreamOnFileEx, the type of the last argument is supposed to be IStream **. However the code snippet passes a wil::com_ptr<IStream> * to the function instead of a IStream **. I'm wondering how it works. Does wil::com_ptr<IStream> overload operator & (aka "address of")?

By the way, I cannot find the online manual of wil::com_ptr. Is it the same as winrt::com_ptr struct template (C++/WinRT)? Thanks.

Upvotes: 0

Views: 284

Answers (1)

IInspectable
IInspectable

Reputation: 51355

The Windows Implementation Libraries (WIL) has its documentation published through its repository's wiki. The wil::com_ptr_t class template1 is described on the WinRT and COM wrappers page.

The section on Object management methods lists three class members that allow clients to get the address of the stored interface pointer:

  • T** addressof()
    Returns the address of the internal pointer without releasing the current COM object. Do not use this for _Out_ parameters2 because it will leak the current COM object. For _Out_ parameters, use the & operator, which releases the current COM object before returning the address.
  • T** put()
    Releases the current COM object and returns the address of the internal pointer. Use this method when passing the com_ptr_t as a _Out_ parameter.
  • T** operator&()
    Same as put().

So to answer the literal question: Yes, wil::com_ptr_t does overload operator&() to return the address of the internal interface pointer after doing some housekeeping.

wil::com_ptr_t is unrelated to the winrt::com_ptr class template3, part of the C++/WinRT library. Neither of them are related to ATL's CComPtr or Visual Studio's _com_ptr_t. Those are the four official implementations, and while all of them have the same goal of automating lifetime management of COM objects they are subtly different in their implementations, with surprising consequences4.

The differences are in the following areas:

  • Constructors with an interface pointer argument

    Implementations get to choose whether they assume ownership of the passed in interface pointer, or model shared ownership leaving the caller with the responsibility of Release()-ing the incoming interface pointer.

  • Assignment operators accepting an interface pointer

    Same as above concerning the incoming pointer, with the added twist of having to handle the case where the instance is already holding an interface pointer. Implementations can:

    • Silently drop the currently held interface pointer (the fact that this is a bug doesn't mean that you won't see it)
    • Silently Release() the current interface prior to assigning the incoming interface pointer
    • Throw an exception in case the instance is holding an interface pointer
  • Retrieving the address of the raw interface pointer

    Similar to the assignment above, the case where the smart pointer instance is already holding an interface needs to be handled, one way or another:

    • Silently drop the stored interface (e.g. addressof())
    • Silently Release() the current interface (e.g. put())
    • Throw an exception in case the stored pointer is not a nullptr

If you decide to use a COM smart pointer implementation, make sure you have a firm grasp on its semantics. The same literal piece of C++ code will potentially behave differently depending on the library in use.


1 wil::com_ptr is an alias template for wil::com_ptr_t with the err_policy template argument set to err_exception_policy.

2 The snippet in the question could have safely passed stream.addressof() even though ppstm is marked as an _Out_ parameter. I'm guessing that the author used operator&() instead for consistency, readability, and maintainability.

3 While the WIL and C++/WinRT are independent libraries they can interoperate with surprisingly little effort.

4 We're using a smart pointer, so we can't possibly be the source of the leak

Upvotes: 1

Related Questions