/* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ /* Author: Michael Davidsaver */ /* wrapper around shared_ptr which tracks backwards references. * Can help to find ref. leaks, loops, and other exciting bugs. * See comments in sharedPtr.h */ #ifndef DEBUGPTR_H #define DEBUGPTR_H #if __cplusplus<201103L # error c++11 required #endif #include #include #include #include #include //! User code should test this macro //! before calling epics::debug::shared_ptr::show_refs() #define HAVE_SHOW_REFS namespace epics { namespace debug { struct tracker; class shared_ptr_base; class epicsShareClass ptr_base { friend class shared_ptr_base; template friend class shared_ptr; template friend class weak_ptr; protected: typedef std::shared_ptr track_t; track_t track; ptr_base() noexcept : track() {} ptr_base(const track_t& track) :track(track) {} ptr_base(const ptr_base&) = delete; ptr_base(ptr_base&&) = delete; ptr_base& operator=(const ptr_base&) = delete; public: typedef std::set ref_set_t; void show_refs(std::ostream&, bool self=true, bool weak=false) const; void spy_refs(ref_set_t&) const; }; class epicsShareClass weak_ptr_base : public ptr_base { protected: weak_ptr_base() {} weak_ptr_base(const track_t& track) :ptr_base(track) {} }; class epicsShareClass shared_ptr_base : public ptr_base { protected: shared_ptr_base() noexcept #ifndef EXCEPT_USE_NONE :m_stack(), m_depth(0) #endif {} shared_ptr_base(const track_t& track) :ptr_base(track) #ifndef EXCEPT_USE_NONE ,m_stack(), m_depth(0) #endif {} ~shared_ptr_base() {track_clear();} // add ourselves to tracker void track_new(); // create new tracker if ptr!=nullptr, otherwise clear void track_new(void* ptr); // copy tracker and add ourself void track_assign(const shared_ptr_base& o); void track_clear(); void swap(shared_ptr_base& o); void snap_stack(); #ifndef EXCEPT_USE_NONE void *m_stack[EXCEPT_DEPTH]; int m_depth; // always <= EXCEPT_DEPTH #endif public: void show_stack(std::ostream&) const; }; template class shared_ptr; template class weak_ptr; template class enable_shared_from_this; template inline void do_enable_shared_from_this(const shared_ptr& dest, enable_shared_from_this* self ); template inline void do_enable_shared_from_this(const shared_ptr&, ...) {} template class shared_ptr : public shared_ptr_base { typedef ::std::shared_ptr real_type; real_type real; template friend class shared_ptr; template friend class weak_ptr; // ctor for casts shared_ptr(const real_type& r, const ptr_base::track_t& t) :shared_ptr_base(t), real(r) {track_new();} public: typedef typename real_type::element_type element_type; typedef weak_ptr weak_type; // new NULL shared_ptr() noexcept {} // copy existing same type shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();} // copy existing of implicitly castable type template shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();} // construct around new pointer template explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) { track_new(a); do_enable_shared_from_this(*this, a); } // make strong ref from weak template shared_ptr(const weak_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();} // takeover from unique_ptr template shared_ptr(std::unique_ptr&& a) : shared_ptr_base(), real(a.release()) {track_new();} ~shared_ptr() {} shared_ptr& operator=(const shared_ptr& o) { if(this!=&o) { real = o.real; track_assign(o); } return *this; } template shared_ptr& operator=(const shared_ptr& o) { if(get()!=o.get()) { real = o.real; track_assign(o); } return *this; } void reset() noexcept { real.reset(); track_clear(); } template void reset(A* a, Args ... args) { real.reset(a, args...); track_new(a); do_enable_shared_from_this(*this, a); } void swap(shared_ptr &o) noexcept { if(this!=&o) { real.swap(o.real); shared_ptr_base::swap(o); } } // proxy remaining to underlying shared_ptr T* get() const noexcept { return real.get(); } typename std::add_lvalue_reference::type operator*() const noexcept { return *real; } T* operator->() const noexcept { return real.get(); } long use_count() const noexcept { return real.use_count(); } bool unique() const noexcept { return real.unique(); } explicit operator bool() const noexcept { return bool(real); } bool operator==(const shared_ptr& o) const { return real==o.real; } bool operator!=(const shared_ptr& o) const { return real!=o.real; } bool operator<(const shared_ptr& o) const { return real bool owner_before(const shared_ptr& o) { return real.owner_before(o); } template bool owner_before(const weak_ptr& o) { return real.owner_before(o); } template friend shared_ptr static_pointer_cast(const shared_ptr& src); template friend shared_ptr const_pointer_cast(const shared_ptr& src); template friend shared_ptr dynamic_pointer_cast(const shared_ptr& src); template friend void do_enable_shared_from_this(const shared_ptr& dest, enable_shared_from_this* self ); }; template shared_ptr static_pointer_cast(const shared_ptr& src) { return shared_ptr(std::static_pointer_cast(src.real), src.track); } template shared_ptr const_pointer_cast(const shared_ptr& src) { return shared_ptr(std::const_pointer_cast(src.real), src.track); } template shared_ptr dynamic_pointer_cast(const shared_ptr& src) { return shared_ptr(std::dynamic_pointer_cast(src.real), src.track); } template class weak_ptr : public weak_ptr_base { typedef ::std::weak_ptr real_type; real_type real; template friend class shared_ptr; template friend class weak_ptr; public: typedef typename real_type::element_type element_type; typedef weak_ptr weak_type; // new NULL weak_ptr() noexcept {} // copy existing same type weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {} // copy existing of similar type template weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {} // create week ref from strong ref template weak_ptr(const shared_ptr& o) :weak_ptr_base(o.track), real(o.real) {} ~weak_ptr() {} weak_ptr& operator=(const weak_ptr& o) { if(this!=&o) { real = o.real; track = o.track; } return *this; } template weak_ptr& operator=(const shared_ptr& o) { real = o.real; track = o.track; return *this; } shared_ptr lock() const noexcept { return shared_ptr(real.lock(), track); } void reset() noexcept { track.reset(); real.reset(); } long use_count() const noexcept { return real.use_count(); } bool unique() const noexcept { return real.unique(); } }; template class enable_shared_from_this { mutable weak_ptr xxInternalSelf; template friend void do_enable_shared_from_this(const shared_ptr& dest, enable_shared_from_this* self ); public: shared_ptr shared_from_this() const { return shared_ptr(xxInternalSelf); } }; template inline void do_enable_shared_from_this(const shared_ptr& dest, enable_shared_from_this* self ) { shared_ptr actual(dynamic_pointer_cast(dest)); if(!actual) throw std::logic_error("epics::debug::enabled_shared_from_this fails"); self->xxInternalSelf = actual; } }} // namespace epics::debug template inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr& ptr) { strm<