Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shared_ptr with non-pointer resources

Tags:

In C++11 is it possible to use shared_ptr to control non-pointer resources?


It is possible to use unique_ptr to manage non-pointer resources. This is done by implementing a custom deleter class which provides:

  1. A typedef {TYPE} pointer; where {TYPE} is the non-pointer resource type
  2. operator()(pointer) which frees the controlled resource

...and then instantiating a unique_ptr with the custom deleter as the second template parameter.

For example, under Windows it is possible to create a unique_ptr which manages a service control handle. This handle type is not freed by calling delete, but by calling CloseServiceHandle(). Here is sample code which does this:

Custom Deleter

struct SvcHandleDeleter {     typedef SC_HANDLE pointer;     SvcHandleDeleter() {};      template<class Other> SvcHandleDeleter(const Other&) {};      void operator()(pointer h) const     {         CloseServiceHandle(h);     } };   typedef std::unique_ptr<SC_HANDLE,SvcHandleDeleter> unique_sch; 

Instantiation

unique_sch scm(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)); 

Is it possible to use shared_ptr to control a non-pointer resource as well?

According to the documentation, there are shared_ptr constructor overloads which take provide the means to provide a custom deleter class, but none of the constructors accept a resource type that is not either a pointer or a wrapper around a pointer.

How can this be done?

like image 254
John Dibling Avatar asked Jul 25 '12 14:07

John Dibling


1 Answers

Sadly, shared_ptr's need for type-erasure makes it impossible with the current interface to achieve exactly what you want. unique_ptr manages to do that because it has static information on the actual deleter type, from where it can draw the actual "pointer" type. In shared_ptr's case, the deleter type is lost in the type-erasure process (which is why you can't specify it in the shared_ptr template).

Also note that unique_ptr doesn't provide any converting constructors like shared_ptr does (e.g. template<class Y> shared_ptr(Y* p)). It can't do so because pointer is not necessarily a real pointer type, and so it can't restrict what can be accepted (except maybe through some SFINAE with std::is_convertible_to or something like that... but I digress).

Now, one obvious workaround is to simply new the resource handle, as dumb as it sounds. :/

std::shared_ptr<SC_HANDLE> sp(new SC_HANDLE(::OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)),     [](SC_HANDLE* p){ ::CloseServiceHandle(*p); delete p; }); 
like image 82
Xeo Avatar answered Oct 07 '22 07:10

Xeo