开发者

std::unique_ptr and pointer-to-pointer

开发者 https://www.devze.com 2023-01-31 13:59 出处:网络
I\'ve been using std::unique_ptr to store some COM resources, and provided a custom deleter function. However, many of the COM functions want pointer-to-pointer. Right now, I\'m using the implementati

I've been using std::unique_ptr to store some COM resources, and provided a custom deleter function. However, many of the COM functions want pointer-to-pointer. Right now, I'm using the implementation detail of _Myptr,开发者_运维百科 in my compiler. Is it going to break unique_ptr to be accessing this data member directly, or should I store a gajillion temporary pointers to construct unique_ptr rvalues from?


COM objects are reference-countable by their nature, so you shouldn't use anything except reference-counting smart pointers like ATL::CComPtr or _com_ptr_t even if it seems inappropriate for your usecase (I fully understand your concerns, I just think you assign too much weight to them). Both classes are designed to be used in all valid scenarios that arise when COM objects are used, including obtaining the pointer-to-pointer. Yes, that's a bit too much functionality, but if you don't expect any specific negative consequences you can't tolerate you should just use those classes - they are designed exactly for this purpose.


I've had to tackle the same problem not too long ago, and I came up with two different solutions:

The first was a simple wrapper that encapsulated a 'writeable' pointer and could be std::moved into my smart pointer. This is just a little more convenient that using the temp pointers you are mentioning, since you cannot define the type directly at the call-site.

Therefore, I didn't stick with that. So what I did was a Retrieve helper-function that would get the COM function and return my smart-pointer (and do all the temporary pointer stuff internally). Now this trivially works with free-functions that only have a single T** parameter. If you want to use this on something more complex, you can just pass in the call via std::bind and only leave the pointer-to-be-returned free.

I know that this is not directly what you're asking, but I think it's a neat solution to the problem you're having.

As a side note, I'd prefer boost's intrusive_ptr instead of std::unique_ptr, but that's a matter of taste, as always.

Edit: Here's some sample code that's transferred from my version using boost::intrusive_ptr (so it might not work out-of-the box with unique_ptr)

template <class T, class PtrType, class PtrDel>
HRESULT retrieve(T func, std::unique_ptr<PtrType, PtrDel>& ptr)
{
  ElementType* raw_ptr=nullptr;

  HRESULT result = func(&raw_ptr);
  ptr.reset(raw_ptr);

  return result;
}

For example, it can be used like this:

std::unique_ptr<IFileDialog, ComDeleter> FileDialog;
/*...*/
using std::bind;
using namespace std::placeholders;
std::unique_ptr<IShellItem, ComDeleter> ShellItem;
HRESULT status = retrieve(bind(&IFileDialog::GetResult, FileDialog, _1), ShellItem);

For bonus points, you can even let retrieve return the unique_ptr instead of taking it by reference. The functor that bind generates should have signature typedefs to derive the pointer type. You can then throw an exception if you get a bad HRESULT.


C++0x smart pointers have a portable way to get at the raw pointer container .get() or release it entirely with .release(). You could also always use &(*ptr) but that is less idiomatic.

If you want to use smart pointers to manage the lifetime of an object, but still need raw pointers to use a library which doesn't support smart pointers (including standard c library) you can use those functions to most conveniently get at the raw pointers.

Remember, you still need to keep the smart pointer around for the duration you want the object to live (so be aware of its lifetime).

Something like:

call_com_function( &my_uniq_ptr.get() ); // will work fine
return &my_localscope_uniq_ptr.get();    // will not
return &my_member_uniq_ptr.get();        // might, if *this will be around for the duration, etc..

Note: this is just a general answer to your question. How to best use COM is a separate issue and sharptooth may very well be correct.


Use a helper function like this.

template< class T >
T*& getPointerRef ( std::unique_ptr<T> & ptr )
{
    struct Twin : public std::unique_ptr<T>::_Mybase {};
    Twin * twin = (Twin*)( &ptr );
    return twin->_Myptr;
}

check the implementation

int wmain ( int argc, wchar_t argv[] )
{   
    std::unique_ptr<char> charPtr ( new char[25] );
    delete getPointerRef(charPtr);
    getPointerRef(charPtr) = 0;

    return charPtr.get() != 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消