|
@@ -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
|