Search code examples
c++11gcccomshared-ptr

shared_ptr and COM object


I have a COM object named factory (IFactory), and it has several methods like

virtual HRESULT STDMETHODCALLTYPE CreateDatabase(IDatabase** Result) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateProcessor(IProcessor** Result) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateDocument(IDocument** Result) = 0;
.....

when I need create a IDocument, I need to do this:

IDocument* doc = nullptr;
factory->CreateDocument(&doc);
// some code here;
doc.Release();

So I want to create a common function to do this and generate a shared_ptr, so taht I do not need to release it manually.

I created a function like this:

template <typename T, typename K>
shared_ptr<T> create_new(K* p, HRESULT (K::*member)(T**)) {
    T* pointer = nullptr;
    (void)p->*member(&pointer);
    shared_ptr<T> result(pointer, [=](T* o) {
        o->Release();
    });
    return result;
}

This way, when I need create a new IDocument I just need:

auto doc = create_new(factory, &IFactory::CreateDocument);
// do not need release it manually

but this does not work, because the compiler needs more information to instantiate the template, so I use

auto doc = create_new(factory, (HRESULT (IFactory::*)(IDocument**))&IFactory::CreateDocument);

this way seems correct, but when I compile my code, the compiler stops at

(void)p->*member(&pointer);

and says:

must use '.*' or '->*' to call pointer-to-member function in 'member (...)', e.g. '(... ->* member) (...)'

In instantiation of 'std::shared_ptr<_Tp1> create_new(K*, HRESULT (K::*)(T**)) [with T = IDocument; K = IFactory; HRESULT = long int]'

Could someone help me figure this out?

Compiler is MinGW-w64 GCC 4.8.5, but I tried GCC 5.3.0, with the same output.


Solution

  • First off, function calls bind tighter than member pointer dereference, so you need these parentheses:

    (p->*member)(&pointer);
    

    (Add cast to void as you prefer).

    Second, you can improve your call syntax by specifying the argument for T explicitly; you should then not need the horrible cast:

    auto doc = create_new<IDocument>(factory, &IFactory::CreateDocument);