I created this shared pointer mainly to use it as RAII for handles that have to be shared between classes or threads
for pointers : it supports raw pointers and pointer to classes and arrays and also support classes inheriting from ref_base class to save an allocation for the reference counter
for non pointers types : they must be nullable and aren’t arrays or inheriting from ref_base
this is my code :
#include <atomic> #include <type_traits> #include <Windows.h> using namespace std; #define HAS_TYPE(NAME) \ template<typename, typename = void> \ struct has_type_##NAME: std::false_type \ {}; \ template<typename T> \ struct has_type_##NAME<T, void_t<typename T::NAME>>: std::true_type \ {} \ HAS_TYPE(pointer); template <class T, bool is_it_array = false> struct default_sp_deleter { void operator()(T *ptr) { delete ptr; } }; template <class T> struct default_sp_deleter<T, true> { void operator()(T *ptr) { delete[] ptr; } }; template <class T, class D, bool inherit, bool has_pointer> struct sp_data {}; template <class T, class D> struct sp_data<T, D, true, true> { using pointer = typename D::pointer; pointer ptr; D deleter; sp_data() : ptr(pointer()) {} sp_data(pointer p) : ptr(p) {} sp_data(pointer p, D del) : ptr(p), deleter(del) {} }; template <class T, class D> struct sp_data<T, D, true, false> { using pointer = T*; pointer ptr; D deleter; sp_data() : ptr(pointer()) {} sp_data(pointer p) : ptr(p) {} sp_data(pointer p, D del) : ptr(p), deleter(del) {} }; template <class T, class D> struct sp_data<T, D, false, false> { using pointer = T*; pointer ptr; D deleter; atomic<uintptr_t> *ref_count; sp_data() : ptr(pointer()), ref_count(new atomic<uintptr_t>(0)) {} sp_data(pointer p) : ptr(p), ref_count(new atomic<uintptr_t>(0)) {} sp_data(pointer p, D del) : ptr(p), deleter(del), ref_count(new atomic<uintptr_t>(0)) {} sp_data(const sp_data<T, D, false, false>& data) : ptr(data.ptr), ref_count(data.ref_count) {} ~sp_data() {} sp_data& operator=(const sp_data<T, D, false, false>& data) { ptr = data.ptr; ref_count = data.ref_count; return *this; } }; template <class T, class D> struct sp_data<T, D, false, true> { using pointer = typename D::pointer; pointer ptr; D deleter; atomic<uintptr_t> *ref_count; sp_data() : ptr(pointer()), ref_count(new atomic<uintptr_t>(0)) {} sp_data(pointer p) : ptr(p), ref_count(new atomic<uintptr_t>(0)) {} sp_data(pointer p, D del) : ptr(p), deleter(del), ref_count(new atomic<uintptr_t>(0)) {} sp_data(const sp_data<T, D, false, false>& data) : ptr(data.ptr), ref_count(data.ref_count) {} template <class U> sp_data(const sp_data<U, D, false, false>& data) : ptr(data.ptr) { } ~sp_data() {} sp_data& operator=(const sp_data<T, D, false, false>& data) { ref_count = data.ref_count; ptr = data.ptr; return *this; } }; class ref_base; template <class T, class Deleter = default_sp_deleter<typename std::remove_all_extents<T>::type, std::is_array<T>::value>> class sp { using elem_type = typename std::remove_all_extents<T>::type; using pointer = typename sp_data<elem_type, Deleter, std::is_base_of<ref_base, T>::value, has_type_pointer<Deleter>::value>::pointer; sp_data<elem_type, Deleter, std::is_base_of<ref_base, pointer>::value, has_type_pointer<Deleter>::value> data; public : using element_type = typename std::remove_pointer_t<pointer>; template<typename, typename> friend class sp; sp() {} sp(pointer p) { data.ptr = p; if (get() != pointer()) inc_ref(); } sp(const sp<T, Deleter>& rhs) : data(rhs.data) { if (get() != pointer()) inc_ref(); } sp(sp<T, Deleter>&& rhs) : data(rhs.data) { rhs.data.ptr = pointer(); } template <class U> sp(const sp<U, Deleter>& rhs) : data(rhs.data) { if (get() != pointer()) inc_ref(); } template <class U> sp(sp<U, Deleter>&& rhs) : data(rhs.data) { rhs.data.ptr = pointer(); } template <class U, typename std::enable_if<std::is_convertible<U, pointer>::value && !std::is_same<U, pointer>::value, bool>::type = true> sp(U value) { data.ptr = value; if (get() != pointer()) inc_ref(); } ~sp() { reset(); } sp<T, Deleter>& operator=(const sp<T, Deleter>& rhs) { reset(); data = rhs.data; if (get() != pointer()) inc_ref(); return *this; } sp<T, Deleter>& operator=(sp<T, Deleter>&& rhs) { reset(); data = rhs.data; if (get() != pointer()) inc_ref(); rhs.reset(); return *this; } template <class U> sp<T, Deleter>& operator=(const sp<U, Deleter>& rhs) { reset(); data = rhs.data; if (get() != pointer()) inc_ref(); return *this; } template <class U> sp<T, Deleter>& operator=(sp<U, Deleter>&& rhs) { reset(); data = rhs.data; if (get() != pointer()) inc_ref(); rhs.reset(); return *this; } pointer get() const { return data.ptr; } template <class U = pointer> typename std::enable_if<is_pointer<U>::value, element_type&>::type operator*() { return *data.ptr; } template <class U = pointer> typename std::enable_if<!std::is_pointer<U>::value, pointer&>::type operator&() { return &data.ptr; } pointer operator->() { return get(); } long use_count() { if (get() == pointer()) return 0; return private_use_count(); } bool unique() { return use_count() == 1; } operator bool() { return get() != pointer(); } Deleter& get_deleter() { return data.deleter; } void reset(pointer ptr = pointer()) { if (get() == ptr) return; if (has_counter() && !dec_ref()) { get_deleter()(get()); } release_ref_counter(); data.ptr = ptr; if (get() != pointer()) { setup_counter(); inc_ref(); } } private : template<class U = element_type> typename enable_if<is_base_of<ref_base, U>::value && std::is_pointer<pointer>::value>::type inc_ref() { get()->inc_ref(); } // currently this type of members is broken and shouldn't be used : // if Deleter::pointer exists it mustn't inherit from ref_base template<class U = element_type> typename enable_if<is_base_of<ref_base, U>::value && !std::is_pointer<pointer>::value>::type inc_ref() { data.ptr.inc_ref(); } template<class U = element_type> typename enable_if<!is_base_of<ref_base, U>::value>::type inc_ref() { ++(*data.ref_count); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value && std::is_pointer<pointer>::value, uintptr_t>::type dec_ref() { return get()->dec_ref(); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value && !std::is_pointer<pointer>::value, uintptr_t>::type dec_ref() { return data.ptr.dec_ref(); } template<class U = element_type> typename std::enable_if<!std::is_base_of<ref_base, U>::value, uintptr_t>::type dec_ref() { --(*data.ref_count); return data.ref_count->load(); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value && std::is_pointer<pointer>::value, uintptr_t>::type private_use_count() { return get()->use_count(); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value && !std::is_pointer<pointer>::value, uintptr_t>::type private_use_count() { return data.ptr.use_count(); } template<class U = element_type> typename std::enable_if<!std::is_base_of<ref_base, U>::value, uintptr_t>::type private_use_count() { return data.ref_count->load(); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value>::type release_ref_counter() {} template<class U = element_type> typename std::enable_if<!std::is_base_of<ref_base, U>::value>::type release_ref_counter() { if (data.ref_count && !(*data.ref_count)) { delete data.ref_count; } data.ref_count = nullptr; } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value>::type setup_counter() {} template<class U = element_type> typename std::enable_if<!std::is_base_of<ref_base, U>::value>::type setup_counter() { data.ref_count = new atomic<uintptr_t>(0); } template<class U = element_type> typename std::enable_if<std::is_base_of<ref_base, U>::value, bool>::type has_counter() { return get() != pointer(); } template<class U = element_type> typename std::enable_if<!std::is_base_of<ref_base, U>::value, bool>::type has_counter() { return data.ref_count != nullptr; } }; class ref_base { public : template<typename, typename> friend class sp; ref_base() : ref_count(0) {} ref_base(const ref_base&) : ref_count(0) {} ref_base(ref_base&&) : ref_count(0) {} ~ref_base() {} ref_base& operator=(const ref_base&) { return *this; } ref_base& operator=(ref_base&&) { return *this; } private : uintptr_t inc_ref() { ++ref_count; return ref_count.load(); } uintptr_t dec_ref() { --ref_count; return ref_count.load(); } long use_count() { return ref_count.load(); } atomic<uintptr_t> ref_count; }; class test : public ref_base { public : test() {} ~test() { cout << "~test()" << endl; } }; class test2 { public : test2() {} ~test2() { cout << "~test2()" << endl; } }; template <typename T, T TNul = T(), bool pseudo = false> class UniqueHandle { public: UniqueHandle(std::nullptr_t = nullptr) :m_id(TNul) { } UniqueHandle(T x) :m_id(x) { // if file handle but not process or thread one prevent -1 if (!pseudo && m_id == INVALID_HANDLE_VALUE) m_id = TNul; } explicit operator bool() const { return m_id != TNul; } operator T&() { return m_id; } operator T() const { return m_id; } T *operator&() { return &m_id; } const T *operator&() const { return &m_id; } friend bool operator == (UniqueHandle a, UniqueHandle b) { return a.m_id == b.m_id; } friend bool operator != (UniqueHandle a, UniqueHandle b) { return a.m_id != b.m_id; } friend bool operator == (UniqueHandle a, std::nullptr_t) { return a.m_id == TNul; } friend bool operator != (UniqueHandle a, std::nullptr_t) { return a.m_id != TNul; } friend bool operator == (std::nullptr_t, UniqueHandle b) { return TNul == b.m_id; } friend bool operator != (std::nullptr_t, UniqueHandle b) { return TNul != b.m_id; } private: T m_id; }; template <class HandleType, class DeleterType, DeleterType Deleter, HandleType null_handle = HandleType(), bool pseudo = false> struct UniqueHandleDeleter { using pointer = UniqueHandle<HandleType, null_handle, pseudo>; void operator()(pointer handle) { if (pseudo) { // pseudo handle is valid but isn't to be closed if (handle == pointer(GetCurrentProcess())) return; } Deleter(handle); } }; using SHandle = sp<int, UniqueHandleDeleter < HANDLE, decltype(&CloseHandle), CloseHandle>>; using SProcHandle = sp<int, UniqueHandleDeleter<HANDLE, decltype(&CloseHandle), CloseHandle, nullptr, true>>; using SRegHandle = sp<int, UniqueHandleDeleter<HKEY, decltype(&RegCloseKey), RegCloseKey>>; using SSocket = sp<int, UniqueHandleDeleter<SOCKET, decltype(&closesocket), closesocket, -1>>;