2

I am trying to using the standard C++ library smart pointers with a library which uses MS COM for most of its function (I must say I am not well-versed with COM). So, I have the following custom deleter for my unique_ptr

struct COMDeleter {
    template<typename T> void operator()(T* ptr) {
        if (ptr) ptr->Release();
    }
};

In the sample code, we have something like:

class MyClass
{
public:
    MyClass(IDeckLink * device) 
      : m_deckLink(device)
    {            
    }  

    MyClass::~MyClass()
    {
        if (m_deckLink != NULL)
        {
            m_deckLink->Release();
            m_deckLink = NULL;
        }
    } 

    IDeckLink * m_deckLink;
};

This could be replaced with:

class MyClass
{
public:
    MyClass(IDeckLink * device) 
    {  
        m_deckLink.reset(device);
    }   
    std::unique_ptr<IDeckLink, COMDeleter>  m_deckLink;
};

Now, I have another interface called IDeckLinkInput which I would like to wrap in a similar way but the way this is initialized is different as follows:

IDeckLinkInput* m_deckLinkInput = NULL;
if (m_deckLink->QueryInterface(IID_IDeckLinkInput, (void**) &m_deckLinkInput) != S_OK)
        return false;

So, if I have a smart-pointer like:

std::unique_ptr<IDeckLinkInput, COMDeleter> m_deckLinkInput(nullptr);

I am not sure how I can use it with initialisation function like the above? Can it even be done or should i just stick to old style C++?

Galik
  • 47,303
  • 4
  • 80
  • 117
Luca
  • 10,458
  • 24
  • 107
  • 234
  • 5
    Microsoft provides a couple of smart pointer classes for working with COM pointers, is there a reason you don't want to use one of those? This seems like a solution to a problem that doesn't exist. – Mark Ransom Feb 27 '17 at 14:47
  • 4
    I would recommend using `CComPtr` for COM. Similar question is already on SO - [see this question](http://stackoverflow.com/questions/21820301/using-stdunique-ptr-for-managing-com-objects) – mpiatek Feb 27 '17 at 14:48
  • Thank you! Sorry was not aware of CComPtr. Should have looked into that. Just started using COM... – Luca Feb 27 '17 at 14:49
  • 1
    Nitpick: smart pointers are not part of the STL, whether it be the original STL or the standard library adoption. – chris Feb 27 '17 at 14:49
  • 1
    @chris a lot of people use STL to refer to the template-based portion of the standard library. It's not proper usage, but it's extremely common. See http://stackoverflow.com/questions/5205491/whats-the-difference-between-stl-and-c-standard-library/5205571 for the whole mess. – Mark Ransom Feb 27 '17 at 16:52
  • 1
    Also see http://stackoverflow.com/questions/5634996/which-com-smart-pointer-classes-to-use – Mark Ransom Feb 27 '17 at 17:00

1 Answers1

3

Something like this:

template<class U, class T>
std::unique_ptr<U, COMDeleter>
upComInterface( GUID guid, T const& src ) {
  if (!src) return {};
  T* r = nullptr;
  if (src->QueryInterface( guid, (void**)&r) != S_OK)
    return {};
  return {r, {}};
}

then we:

auto deckLink = upComInterface<IDeckLinkInput>( IID_IDeckLinkInput, deckLink );

There is a minor DRY violation here -- the link between IDeckLinkInput and IID_IDeckLinkInput has to be repeated each time you do this, and getting it wrong leads to undefined behavior.

We can fix this via a number of mechanisms. Personally, I'd go with a tag dispatch type:

namespace MyComHelpers {
  template<class T> struct com_tag_t {using type=T; constexpr com_tag_t(){};};
  template<class T> constexpr com_tag_t<T> com_tag{};

  template<class T>
  constexpr void get_com_guid( com_tag_t<T> ) = delete; // overload this for your particular types

  template<class T>
  constexpr GUID interface_guid = get_com_guid( com_tag<T> );
}

Now we can associate the type with a guid. In the namespace of IDeckLinkInput do this:

constexpr GUID get_com_guid( MyComHelpers::com_tag_t<IDeckLinkInput> ) {
  // constexpr code that returns the GUID
}

we then rewrite the get interface function:

std::unique_ptr<U, COMDeleter>
com_cast( T const& src ) {
  if (!src) return {};
  T* r = nullptr;
  if (src->QueryInterface( MyComHelpers::interface_guid<T>, (void**)&r) != S_OK)
    return {};
  return {r, {}};
}

and use becomes:

auto declLink = com_cast<IDeckLinkInput>(m_deckLinkInput);

There are many ways to associate the type with the guid, including traits classes. The constexpr ADL-based lookup function and variable template is just one way.

Code not tested.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524