Эх сурвалжийг харах

feat(libstdc++): add shared_ptr

greatbridf 1 жил өмнө
parent
commit
47934b45b5
1 өөрчлөгдсөн 251 нэмэгдсэн , 0 устгасан
  1. 251 0
      gblibstdc++/include/memory

+ 251 - 0
gblibstdc++/include/memory

@@ -360,6 +360,257 @@ struct allocator_traits {
     { return __helpers::allocator_select_on_copy<Allocator>::get(alloc); }
 };
 
+// TODO: weak_ptr
+template <typename T>
+class shared_ptr {
+public:
+    using element_type = std::remove_extent_t<T>;
+    using pointer = element_type*; // TODO: pointer_traits
+    using const_pointer = const element_type*;
+    using reference = element_type&;
+    using const_reference = const element_type&;
+
+private:
+    struct control_block_base {
+        std::size_t ref_count;
+        std::size_t weak_count;
+        pointer ptr;
+
+        constexpr control_block_base(std::size_t ref_count,
+            std::size_t weak_count, pointer ptr)
+            : ref_count(ref_count), weak_count(weak_count), ptr(ptr) { }
+
+        virtual constexpr ~control_block_base() = default;
+        virtual constexpr void do_delete() = 0;
+    };
+
+    template <typename Deleter>
+    struct control_block : public virtual control_block_base {
+        Deleter deleter;
+        virtual constexpr ~control_block() = default;
+
+        template <typename UDeleter>
+        constexpr control_block(std::size_t ref_count,
+            std::size_t weak_count, pointer ptr, UDeleter&& deleter)
+            : control_block_base { ref_count, weak_count, ptr }
+            , deleter(std::forward<UDeleter>(deleter)) { }
+
+        virtual constexpr void do_delete() override
+        {
+            if (this->ptr)
+                deleter(this->ptr);
+            this->ptr = nullptr;
+        }
+    };
+
+    struct default_control_block : public virtual control_block_base {
+        virtual constexpr ~default_control_block() = default;
+
+        constexpr default_control_block(std::size_t ref_count,
+            std::size_t weak_count, pointer ptr)
+            : control_block_base { ref_count, weak_count, ptr } { }
+
+        virtual constexpr void do_delete() override
+        {
+            if (this->ptr)
+                delete this->ptr;
+            this->ptr = nullptr;
+        }
+    };
+
+    control_block_base* cb { };
+
+    void inc_ref()
+    {
+        if (cb)
+            ++cb->ref_count; // TODO: lock and atomic
+    }
+
+    void dec_ref()
+    {
+        if (cb && --cb->ref_count == 0) {
+            cb->do_delete();
+            if (cb->weak_count == 0)
+                delete cb;
+        }
+    }
+
+private:
+    template <typename Deleter>
+    using rebind_allocator = typename std::allocator_traits<Deleter>::template
+        rebind_alloc<control_block<Deleter>>;
+
+    template <typename U>
+    friend class shared_ptr;
+
+public:
+    constexpr shared_ptr() noexcept = default;
+    constexpr shared_ptr(std::nullptr_t) noexcept : cb { } { }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    explicit shared_ptr(U* ptr) // TODO: array type
+        : cb(new default_control_block { 1, 0, ptr }) { }
+
+    template <typename U, typename Deleter>
+    __GBLIBCPP_CONSTEXPR
+    explicit shared_ptr(U* ptr, Deleter d)
+        : cb(new control_block<Deleter> { 1, 0, ptr, d }) { }
+
+    template <typename Deleter>
+    __GBLIBCPP_CONSTEXPR
+    explicit shared_ptr(std::nullptr_t, Deleter d)
+        : cb(new control_block<Deleter> { 1, 0, nullptr, d }) { }
+
+    // TODO: what the fuck
+    // template <typename U, typename Deleter, typename Allocator>
+    // __GBLIBCPP_CONSTEXPR
+    // explicit shared_ptr(U* ptr, Deleter d, Allocator alloc)
+    // {
+    //     cb = std::allocator_traits<
+    //         rebind_allocator<Deleter>>::allocate(alloc, 1);
+
+    //     std::allocator_traits<
+    //         rebind_allocator<Deleter>>::construct(alloc, cb, 1, 0, ptr, d);
+    // }
+
+    // template <typename Deleter, typename Allocator>
+    // __GBLIBCPP_CONSTEXPR
+    // explicit shared_ptr(std::nullptr_t, Deleter d, Allocator alloc)
+    // {
+    //     cb = std::allocator_traits<
+    //         rebind_allocator<Deleter>>::allocate(alloc, 1);
+
+    //     std::allocator_traits<
+    //         rebind_allocator<Deleter>>::construct(alloc, cb, 1, 0, nullptr, d);
+    // }
+
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr(const shared_ptr& other) noexcept
+        : cb(other.cb) { inc_ref(); }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr(const shared_ptr<U>& other) noexcept
+        : cb(other.cb) { inc_ref(); }
+
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr(shared_ptr&& other) noexcept
+        : cb(std::exchange(other.cb, nullptr)) { }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr(shared_ptr<U>&& other) noexcept
+        : cb(std::exchange(other.cb, nullptr)) { }
+    
+    // TODO: weak_ptr and unique_ptr
+
+    __GBLIBCPP_CONSTEXPR
+    ~shared_ptr() { dec_ref(); }
+
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr& operator=(const shared_ptr& other) noexcept
+    {
+        if (cb != other.cb) {
+            dec_ref();
+            cb = other.cb;
+            inc_ref();
+        }
+        return *this;
+    }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr& operator=(const shared_ptr<U>& other) noexcept
+    {
+        if (cb != other.cb) {
+            dec_ref();
+            cb = other.cb;
+            inc_ref();
+        }
+        return *this;
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr& operator=(shared_ptr&& other) noexcept
+    {
+        if (cb != other.cb) {
+            dec_ref();
+            cb = std::exchange(other.cb, nullptr);
+        }
+        return *this;
+    }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    shared_ptr& operator=(shared_ptr<U>&& other) noexcept
+    {
+        if (cb != other.cb) {
+            dec_ref();
+            cb = std::exchange(other.cb, nullptr);
+        }
+        return *this;
+    }
+
+    __GBLIBCPP_CONSTEXPR
+    element_type* get() const noexcept
+    { return cb ? cb->ptr : nullptr; }
+
+    __GBLIBCPP_CONSTEXPR
+    explicit operator bool() const noexcept
+    { return get(); }
+
+    __GBLIBCPP_CONSTEXPR
+    T& operator*() const noexcept { return *get(); }
+    __GBLIBCPP_CONSTEXPR
+    T* operator->() const noexcept { return get(); }
+    __GBLIBCPP_CONSTEXPR
+    element_type& operator[](std::size_t i) const noexcept { return get()[i]; }
+
+    __GBLIBCPP_CONSTEXPR
+    long use_count() const noexcept { return cb ? cb->ref_count : 0; }
+
+    __GBLIBCPP_CONSTEXPR
+    bool owner_before(const shared_ptr& other) const noexcept
+    { return cb < other.cb; }
+
+    __GBLIBCPP_CONSTEXPR
+    void swap(shared_ptr& other) noexcept { std::swap(cb->ptr, other.cb->ptr); }
+
+    __GBLIBCPP_CONSTEXPR
+    void reset() noexcept { dec_ref(); cb = nullptr; }
+
+    template <typename U>
+    __GBLIBCPP_CONSTEXPR
+    void reset(U* ptr) noexcept
+    { dec_ref(); cb = new default_control_block { 1, 0, ptr }; }
+
+    template <typename U, typename Deleter>
+    __GBLIBCPP_CONSTEXPR
+    void reset(U* ptr, Deleter d) noexcept
+    { dec_ref(); cb = new control_block<Deleter> { 1, 0, ptr, d }; }
+
+    // TODO: what the fuck
+    // template <typename U, typename Deleter, typename Allocator>
+    // __GBLIBCPP_CONSTEXPR
+    // void reset(U* ptr, Deleter d, Allocator alloc)
+    // {
+    //     dec_ref();
+    //     cb = std::allocator_traits<
+    //         rebind_allocator<Deleter>>::allocate(alloc, 1);
+
+    //     std::allocator_traits<
+    //         rebind_allocator<Deleter>>::construct(alloc, cb, 1, 0, ptr, d);
+    // }
+};
+
+// TODO: use only one allocation
+// template <typename T, typename... Args>
+// std::shared_ptr<T> make_shared(Args&&... args)
+// {
+//     return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
+// }
+
 } // namespace std
 
 #endif