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++?

6
  • 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. Commented Feb 27, 2017 at 14:47
  • 4
    I would recommend using CComPtr for COM. Similar question is already on SO - see this question Commented Feb 27, 2017 at 14:48
  • 1
    Nitpick: smart pointers are not part of the STL, whether it be the original STL or the standard library adoption. Commented Feb 27, 2017 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 stackoverflow.com/questions/5205491/… for the whole mess. Commented Feb 27, 2017 at 16:52
  • 1
    Also see stackoverflow.com/questions/5634996/… Commented Feb 27, 2017 at 17:00

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.