/** * Copyright (c) 2018 Cornell University. * * Author: Ted Yin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _SALTICIDAE_REF_H #define _SALTICIDAE_REF_H #include #include namespace salticidae { template struct default_delete { constexpr default_delete() = default; void operator()(T *ptr) const { static_assert(!std::is_void::value, "can't delete pointer to incomplete type"); static_assert(sizeof(T) > 0, "can't delete pointer to incomplete type"); delete ptr; } }; template struct default_delete { constexpr default_delete() = default; void operator()(T *ptr) const { static_assert(sizeof(T) > 0, "can't delete pointer to incomplete type"); delete [] ptr; } }; template class _RcObjBase; template class _BoxObj; template class BoxObj; template class _BoxObj { protected: T *obj; template friend class _RcObjBase; template friend class _BoxObj; public: using type = T; constexpr _BoxObj(): obj(nullptr) {} constexpr _BoxObj(T *obj): obj(obj) {} _BoxObj(const _BoxObj &other) = delete; _BoxObj(_BoxObj &&other): obj(other.obj) { other.obj = nullptr; } template _BoxObj(_BoxObj &&other): obj(other.obj) { other.obj = nullptr; } ~_BoxObj() { if (obj) D()(obj); } void swap(_BoxObj &other) { std::swap(obj, other.obj); } _BoxObj &operator=(const _BoxObj &other) = delete; _BoxObj &operator=(_BoxObj &&other) { if (this != &other) { _BoxObj tmp(std::move(other)); tmp.swap(*this); } return *this; } T &operator *() const { return *obj; } T *get() const { return obj; } T *unwrap() { auto ret = obj; obj = nullptr; return ret; } operator bool() const { return obj != nullptr; } bool operator==(const _BoxObj &other) const { return obj == other.obj; } bool operator!=(const _BoxObj &other) const { return obj != other.obj; } bool operator==(std::nullptr_t) const { return obj == nullptr; } bool operator!=(std::nullptr_t) const { return obj != nullptr; } bool operator<(const _BoxObj &other) const { return obj < other.obj; } bool operator>(const _BoxObj &other) const { return obj > other.obj; } bool operator<=(const _BoxObj &other) const { return obj <= other.obj; } bool operator>=(const _BoxObj &other) const { return obj >= other.obj; } }; template> class BoxObj: public _BoxObj { using base_t = _BoxObj; friend std::hash; template friend BoxObj static_pointer_cast(BoxObj &&other); public: BoxObj() = default; BoxObj(T *obj): base_t(obj) {} template::value>::type> BoxObj(BoxObj &&other): base_t(std::move(other)) {} T *operator->() const { return base_t::obj; } }; template class BoxObj: public _BoxObj { using base_t = _BoxObj; friend std::hash; template friend BoxObj static_pointer_cast(BoxObj &&other); public: BoxObj() = default; BoxObj(T obj[]): base_t(obj) {} template BoxObj(BoxObj &&other): base_t(std::move(other)) {} T &operator[](size_t idx) const { return base_t::obj[idx]; } }; template, typename T_, typename D_> BoxObj static_pointer_cast(BoxObj &&other) { BoxObj box{}; box.obj = static_cast::type *>(other.obj); return box; } struct _RCCtl { size_t ref_cnt; size_t weak_cnt; void add_ref() { ref_cnt++; } void add_weak() { weak_cnt++; } bool release_ref() { if (--ref_cnt) return false; if (weak_cnt) return true; delete this; return true; } void release_weak() { if (--weak_cnt) return; if (ref_cnt) return; delete this; } size_t get_cnt() { return ref_cnt; } _RCCtl(): ref_cnt(1), weak_cnt(0) {} ~_RCCtl() {} }; struct _ARCCtl { std::atomic_size_t ref_cnt; std::atomic_size_t weak_cnt; std::atomic dcnt; void add_ref() { ref_cnt++; } void add_weak() { weak_cnt++; } bool release_ref() { dcnt++; if (--ref_cnt) { dcnt--; return false; } if (weak_cnt) { dcnt--; return true; } if (!--dcnt) delete this; return true; } void release_weak() { dcnt++; if (--weak_cnt) { dcnt--; return; } if (ref_cnt) { dcnt--; return; } if (!--dcnt) delete this; } size_t get_cnt() { return ref_cnt.load(); } _ARCCtl(): ref_cnt(1), weak_cnt(0), dcnt(0) {} ~_ARCCtl() {} }; template class RcObjBase; template class _WeakObjBase { T *obj; R *ctl; template friend class _RcObjBase; public: _WeakObjBase(): ctl(nullptr) {} _WeakObjBase(const _WeakObjBase &other): obj(other.obj), ctl(other.ctl) { if (ctl) ctl->add_weak(); } _WeakObjBase(_WeakObjBase &&other): obj(other.obj), ctl(other.ctl) { other.ctl = nullptr; } void swap(_WeakObjBase &other) { std::swap(obj, other.obj); std::swap(ctl, other.ctl); } _WeakObjBase &operator=(const _WeakObjBase &other) { if (this != &other) { _WeakObjBase tmp(other); tmp.swap(*this); } return *this; } _WeakObjBase &operator=(_WeakObjBase &&other) { if (this != &other) { _WeakObjBase tmp(std::move(other)); tmp.swap(*this); } return *this; } template _WeakObjBase(const _RcObjBase &other); ~_WeakObjBase() { if (ctl) ctl->release_weak(); } }; template class WeakObjBase: public _WeakObjBase { using base_t = _WeakObjBase; friend std::hash; public: WeakObjBase() = default; WeakObjBase(const WeakObjBase &other) = default; WeakObjBase(WeakObjBase &&other) = default; template WeakObjBase(const RcObjBase &other): base_t(other) {} WeakObjBase &operator=(const WeakObjBase &) = default; WeakObjBase &operator=(WeakObjBase &&) = default; }; template class WeakObjBase: public _WeakObjBase { using base_t = _WeakObjBase; friend std::hash; public: WeakObjBase() = default; WeakObjBase(const WeakObjBase &other) = default; WeakObjBase(WeakObjBase &&other) = default; template WeakObjBase(const RcObjBase &other): base_t(other) {} WeakObjBase &operator=(const WeakObjBase &) = default; WeakObjBase &operator=(WeakObjBase &&) = default; }; template class _RcObjBase { protected: T *obj; R *ctl; friend _WeakObjBase; template friend class _RcObjBase; public: using type = T; _RcObjBase(): obj(nullptr), ctl(nullptr) {} _RcObjBase(T *obj): obj(obj), ctl(new R()) {} _RcObjBase(BoxObj &&box_ref): obj(box_ref.obj), ctl(new R()) { box_ref.obj = nullptr; } _RcObjBase(const _RcObjBase &other): obj(other.obj), ctl(other.ctl) { if (ctl) ctl->add_ref(); } _RcObjBase(_RcObjBase &&other): obj(other.obj), ctl(other.ctl) { other.ctl = nullptr; } template _RcObjBase(const _RcObjBase &other): obj(other.obj), ctl(other.ctl) { if (ctl) ctl->add_ref(); } template _RcObjBase(_RcObjBase &&other): obj(other.obj), ctl(other.ctl) { other.ctl = nullptr; } void swap(_RcObjBase &other) { std::swap(obj, other.obj); std::swap(ctl, other.ctl); } _RcObjBase &operator=(const _RcObjBase &other) { if (this != &other) { _RcObjBase tmp(other); tmp.swap(*this); } return *this; } _RcObjBase &operator=(_RcObjBase &&other) { if (this != &other) { _RcObjBase tmp(std::move(other)); tmp.swap(*this); } return *this; } _RcObjBase(const _WeakObjBase &other) { if (other.ctl && other.ctl->ref_cnt) { obj = other.obj; ctl = other.ctl; ctl->add_ref(); } else { obj = nullptr; ctl = nullptr; } } ~_RcObjBase() { if (ctl && ctl->release_ref()) D()(obj); } size_t get_cnt() const { return ctl ? ctl->get_cnt() : 0; } T &operator *() const { return *obj; } T *get() const { return obj; } operator bool() const { return obj != nullptr; } bool operator==(const _RcObjBase &other) const { return obj == other.obj; } bool operator!=(const _RcObjBase &other) const { return obj != other.obj; } bool operator==(std::nullptr_t) const { return obj == nullptr; } bool operator!=(std::nullptr_t) const { return obj != nullptr; } bool operator<(const _RcObjBase &other) const { return obj < other.obj; } bool operator>(const _RcObjBase &other) const { return obj > other.obj; } bool operator<=(const _RcObjBase &other) const { return obj <= other.obj; } bool operator>=(const _RcObjBase &other) const { return obj >= other.obj; } }; template> class RcObjBase: public _RcObjBase { using base_t = _RcObjBase; friend std::hash; template friend RcObjBase static_pointer_cast(const RcObjBase &other); template friend RcObjBase static_pointer_cast(RcObjBase &&other); public: T *operator->() const { return base_t::obj; } RcObjBase() = default; RcObjBase(T *obj): base_t(obj) {} RcObjBase(BoxObj &&box_ref): base_t(std::move(box_ref)) {} template::value>::type> RcObjBase(const RcObjBase &other): base_t(other) {} template::value>::type> RcObjBase(RcObjBase &&other): base_t(std::move(other)) {} RcObjBase(const WeakObjBase &other): base_t(other) {} RcObjBase(const RcObjBase &other) = default; RcObjBase(RcObjBase &&other) = default; RcObjBase &operator=(const RcObjBase &) = default; RcObjBase &operator=(RcObjBase &&) = default; }; template class RcObjBase: public _RcObjBase { using base_t = _RcObjBase; friend std::hash; template friend RcObjBase static_pointer_cast(const RcObjBase &other); template friend RcObjBase static_pointer_cast(RcObjBase &&other); public: T &operator[](size_t idx) { return base_t::obj; } RcObjBase() = default; RcObjBase(T obj[]): base_t(obj) {} RcObjBase(BoxObj &&box_ref): base_t(std::move(box_ref)) {} template RcObjBase(const RcObjBase &other): base_t(other) {} template RcObjBase(RcObjBase &&other): base_t(std::move(other)) {} RcObjBase(const WeakObjBase &other): base_t(other) {} RcObjBase(const RcObjBase &other) = default; RcObjBase(RcObjBase &&other) = default; RcObjBase &operator=(const RcObjBase &) = default; RcObjBase &operator=(RcObjBase &&) = default; }; template, typename T_, typename R_, typename D_> RcObjBase static_pointer_cast(const RcObjBase &other) { RcObjBase rc{}; rc.obj = static_cast::type *>(other.obj); if ((rc.ctl = other.ctl)) rc.ctl->add_ref(); return rc; } template, typename T_, typename R_, typename D_> RcObjBase static_pointer_cast(RcObjBase &&other) { RcObjBase rc{}; rc.obj = static_cast::type *>(other.obj); rc.ctl = other.ctl; other.ctl = nullptr; return rc; } template template inline _WeakObjBase::_WeakObjBase(const _RcObjBase &other): obj(other.obj), ctl(other.ctl) { if (ctl) ctl->add_weak(); } template> using RcObj = RcObjBase; template using WeakObj = WeakObjBase; template> using ArcObj = RcObjBase; template using AweakObj = WeakObjBase; } namespace std { template struct hash> { size_t operator()(const salticidae::RcObjBase &k) const { return (size_t)k.obj; } }; template struct hash> { size_t operator()(const salticidae::BoxObj &k) const { return (size_t)k.obj; } }; } #endif