Search code examples
c++comraii

RAII with array of COM objects


Problem:

In COM you occasionally find functions with signatures like this:

HRESULT STDMETHODCALLTYPE GetColorContexts( 
        UINT cCount,
        IWICColorContext **ppIColorContexts,
        UINT *pcActualCount)

The problem this presents for me is that ppIColorContexts must be an initialized array of IWICColorContext *. I have tried referencing the first element of a Vector of ATL::CComPtr<IWICColorContext> with no such luck it won't trigger the () operator so it complains about a type mismatch.

Attempted solutions:

  • vector<ATL::CComPtr<IWICColorContext>> failed due to type mismatch, as noted in the comments this has other issues as CComPtr overloads operator & which breaks STL containers. It seems that this was fixed in C++11 and was included in the STL in VC2010
  • BOOST_SCOPE_EXIT_ALL works but still means I'm manually managing the lifetime of the COM objects which is something I'd like to get away from.

Unattempted solutions:

  • Custom data structure - this is likely what I'll have to do if there is not a more elegant solution, but at least it would allow me to take advantage of destruction semantics properly.
  • Attach a CComPtr after this call - I dislike this solution because it leaves me with a period of execution where the resource may not get released if something goes wrong.
  • std::unique_ptr<IWICColorContext[]> with a custom deleter - I have yet to fully explore this possibility but it would ensure that the COM objects would always get released.

Solution

  • Ultimately the solution was described by igor tandetnik in the comments above:

    Basically in VC2010+ ATL::CComPtr has a sizeof that is the same as the pointer they represent (e.g. sizeof(ATL::CComPtr<IWICColorContext>) == sizeof(IWICColorContext*)), as best I can tell this is because they have no virtual functions and thus need no vTable. This is however highly dangerous as it's relying on a compiler implementation detail. Thus the following works:

    std::vector<ATL::CComPtr<IWICColorContext> > > vec(5);
    // CComPtrs are created and initialized here
    GetColorContexts(vec.size(), &vec[0].m_T, ...);
    

    Mark brought up a very good point that the solution above was completely dependent on compiler implementation which is dangerous. However the solution of only attaching ATL::CComPtr after the GetColorContexts call was not palattable either as it would not have been exception safe.

    Ultimately my solution (tested this morning) is to create a vector<IWICColorContext*> temporarily from the vector<CComPtr<IWICColorContext>> this temporary vector does not increment the ref count and allows me to maintain exception safety.