Last active
September 14, 2023 16:03
-
-
Save jemand2001/5d96083086dc8ca81b51a57f6a6f532b to your computer and use it in GitHub Desktop.
rudimentary shared_ptr implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <atomic> | |
| #include <concepts> | |
| #include <iostream> | |
| #include <stdexcept> | |
| #include <vector> | |
| template <typename T> | |
| class Badge { | |
| friend T; | |
| Badge() = default; | |
| }; | |
| struct bad_control_block_delete : std::logic_error { | |
| using std::logic_error::logic_error; | |
| }; | |
| template <typename, typename> | |
| struct SharedPtr; | |
| template <typename T> | |
| struct default_delete { | |
| void operator()(T *p) { delete p; } | |
| }; | |
| template <typename T, typename Deleter> | |
| requires std::invocable<Deleter, T *> && (!std::is_array_v<T>) | |
| struct ControlBlock { | |
| T *thing; | |
| std::atomic<size_t> shared_count{1}; | |
| std::atomic<size_t> weak_count{0}; | |
| Deleter del; | |
| ~ControlBlock() noexcept(false) { | |
| if (shared_count || weak_count) { | |
| throw bad_control_block_delete{ | |
| "destroying a control block when things are still referring to " | |
| "it"}; | |
| } | |
| destroy_object(); | |
| } | |
| ControlBlock(const ControlBlock &) = delete; | |
| ControlBlock(ControlBlock &&) = delete; | |
| ControlBlock(T *p, Deleter d = {}) : thing{p}, del{d} {} | |
| size_t add_shared() { return shared_count++; } | |
| size_t add_weak() { return weak_count++; } | |
| size_t del_shared() { return shared_count--; } | |
| size_t del_weak() { return weak_count--; } | |
| bool deletable() { return !(shared_count || weak_count); } | |
| void destroy_object() { | |
| // if (thing) | |
| del(thing); | |
| thing = nullptr; | |
| } | |
| // returns a new SharedPtr using this control block | |
| SharedPtr<T, Deleter> manifest(); | |
| }; | |
| template <typename T, typename Deleter = default_delete<T>> | |
| struct SharedPtr { | |
| using Control = ControlBlock<T, Deleter>; | |
| Control *control{nullptr}; | |
| SharedPtr() = default; | |
| SharedPtr(Badge<Control>, Control *block) : control{block} { | |
| if (control) | |
| control->add_shared(); | |
| } | |
| SharedPtr(T *p) : control{new Control{p}} {} | |
| SharedPtr(T *p, Deleter &&d) | |
| : control{new Control{p, std::forward<Deleter>(d)}} {} | |
| SharedPtr(SharedPtr &&other) : control{other.control} { | |
| other.control = nullptr; | |
| } | |
| SharedPtr(const SharedPtr &other) : control{other.control} { | |
| if (control) | |
| control->add_shared(); | |
| } | |
| ~SharedPtr() { | |
| if (!control) | |
| return; | |
| auto count = control->del_shared(); | |
| if (control->deletable()) | |
| delete control; | |
| if (!count) | |
| control->destroy_object(); | |
| } | |
| size_t ref_count() const { return control->shared_count; } | |
| operator bool() const { return control->thing; } | |
| T &operator*() { return *control->thing; } | |
| T *operator->() { return control->thing; } | |
| }; | |
| template <typename T, typename Deleter> | |
| requires std::invocable<Deleter, T *> && (!std::is_array_v<T>) | |
| SharedPtr<T, Deleter> ControlBlock<T, Deleter>::manifest() { | |
| return SharedPtr({}, this); | |
| } | |
| template <typename T, typename Deleter> | |
| struct WeakPtr { | |
| using Control = ControlBlock<T, Deleter>; | |
| using Shared = SharedPtr<T, Deleter>; | |
| Control *control{nullptr}; | |
| WeakPtr() = default; | |
| WeakPtr(const Shared &shared) : control{shared.control} { | |
| if (control) | |
| control->add_weak(); | |
| } | |
| WeakPtr(const WeakPtr &other) : control{other.control} { | |
| if (control) | |
| control->add_weak(); | |
| } | |
| WeakPtr(WeakPtr &&other) : control{other.control} { | |
| other.control = nullptr; | |
| } | |
| Shared lock() { | |
| if (present()) | |
| return control->manifest(); | |
| return {nullptr}; | |
| } | |
| ~WeakPtr() { | |
| if (!control) | |
| return; | |
| control->del_weak(); | |
| if (control->deletable()) | |
| delete control; | |
| } | |
| bool present() const { return control->thing; } | |
| }; | |
| template <typename T, typename D = default_delete<T>> | |
| SharedPtr<T, D> makeShared(auto &&...args) { | |
| return SharedPtr<T, D>(new T{std::forward<decltype(args)>(args)...}); | |
| } | |
| int main() { | |
| std::vector<SharedPtr<int>> vec; | |
| vec.push_back(makeShared<int>(1)); | |
| vec.push_back(makeShared<int>(2)); | |
| vec.push_back(makeShared<int>(3)); | |
| vec.push_back(makeShared<int>(4)); | |
| auto a = vec[0]; | |
| WeakPtr weak = vec[1]; | |
| std::cout << *a << '\n'; | |
| std::cout << a.ref_count() << '\n'; | |
| std::cout << std::boolalpha << weak.present() << '\n'; | |
| auto b = weak.lock(); | |
| std::cout << b.ref_count() << '\n'; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment