diff --git a/include/rcp.h b/include/rcp.h new file mode 100644 index 0000000..bcb3806 --- /dev/null +++ b/include/rcp.h @@ -0,0 +1,123 @@ +#pragma once + +#include + +namespace rcp_internal +{ + class root + { + private: + mutable std::atomic ref_count{0}; + + rcp root_rcp; + + protected: + root() + { + root_rcp = rcp(this); + } + + virtual ~root() = default; + + public: + void add_ref() const + { + ref_count.fetch_add(1, std::memory_order_relaxed); + } + + void release_ref() const + { + if (ref_count.fetch_sub(1, std::memory_order_acq_rel) == 0) + { + delete this; + } + } + + rcp & get_root_rcp() + { + return root_rcp; + } + } + + template + class managed + { + public: + static_assert(std::is_base_of::value, + "T must inherit from rcp::root to use rcp::managed"); + + rcp get_rcp() + { + root * r = (root *)this; + return rcp(r->get_root_ref()); + } + } +} + +/** + * Reference-counting pointer. + */ +template +class rcp +{ +private: + T * ptr = nullptr; + + explicit rcp(T * p) : ptr(p) + { + if (p) + { + ptr->add_ref(); + } + } + + friend T; + +public: + rcp() = default; + + rcp(const rcp & other) : ptr(other.ptr) + { + if (ptr) + { + ptr->add_ref(); + } + } + + ~rcp() + { + if (ptr) + { + ptr->release_ref(); + } + } + + /* Casting constructor: allows rcp -> rcp */ + template + rcp(const rcp & other) : ptr(static_cast(other.get_raw())) + { + if (ptr) + { + ptr->add_ref(); + } + } + + T * get_raw() const + { + return ptr; + } + + T * operator->() const + { + return ptr; + } + + bool operator!() const + { + return !ptr; + } + + using root = rcp_internal::root; + + using managed = rcp_internal::managed; +}