4
\$\begingroup\$

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 to save an allocation for the reference counter.

For non-pointers:
They must be nullable and aren't arrays nor inheriting from ref_base.

#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>>;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.