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