Skip to content

Update com_ptr dereference implementation to account for proyected types #1503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

MiguelBarro
Copy link

The current implementation:

    T& com_ptr<T>::operator*() const noexcept
    {
        return *m_ptr;
    }

cannot work for proyected types because abi_t<T> != T.
Note that proyections provide abi_t specializations. By default abi_t<T> == T and the operator works.

The fix takes into account two cases:

  • The com_ptr<> wraps a COM/WinRT interface, that is T != abi_t<T>.

    com_ptr
      ┕> m_ptr = impl::abi_t<T>*
    

    Because the projected wrapper layout is:

    IXXX
      ┕> m_ptr = impl::unknown_abi*
    

    Then a plain cast would do: return *reinterpret_cast<IXXX*>(const_cast<com_ptr*>(this));

  • The com_ptr<> wraps an implementation object, that is T == abi_t<T>. E.g. an event_array:

    com_ptr
      ┕> m_ptr = EventHandler<int>*
    

    return a reference to the member object: return *const_cast<EventHandler<int>*>(m_ptr);

A regression test is included.

@sylveon
Copy link
Contributor

sylveon commented Aug 19, 2025

You are not meant to wrap projection types in com_ptr. Projection types already implement reference counting semantics.

@MiguelBarro
Copy link
Author

com_ptr looks like the WinRT version of the ATL's CComPtr or the _com_ptr_t extension.
That is, it provides direct access to the underlying COM APIs.

The projection wrappers generated by cppwinrt provide more convenient modern C++ ones and exception handling of
errors.

It seems to me they seek different goals. Besides, note how com_ptr uses abi_t<T> for the underlying pointer.
It was designed with projected classes in mind too.

This pull request is a workaround for an issue I found using delegates.
The projected types have a constructor from com_ptr, for example:

    template <typename O, typename M>
    AsyncActionCompletedHandler::AsyncActionCompletedHandler(com_ptr<O>&& object, M method)
        : AsyncActionCompletedHandler([o = std::move(object), method](auto&&... args)
            { return ((*o).*(method))(args...); })
    {}

But it failed to compile. Eventually I discovered that the issue was that O was a projection type.
And that breaks *o because O != abi_t<O>.
That is frustrating for the user because object should be a valid argument.

A possible workaround would be make cppwinrt replace this lambda with another that takes this issue into account.
I tried using the impl:com_ref alias but as mentioned above com_ptr and the projections do not share APIs and *o is not valid for a projection
class.
I think fixing this issue in the com_ptr implementation is easier and would fix another similar scenarios not yet
uncovered.

@kennykerr
Copy link
Collaborator

Is there a specific problem you're trying to solve?

@sylveon
Copy link
Contributor

sylveon commented Aug 20, 2025

Those handlers are meant to pass an implementation type, see here.

I'm not so sure com_ptr was meant to be used this way with projection types, as you can spot the following line in get_runtime_activation_factory_impl: com_ptr<abi_t<Windows::Foundation::IActivationFactory>> library_factory;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants