#ifndef STAR_GUARDED_PTR_HPP #define STAR_GUARDED_PTR_HPP #include "StarException.hpp" #include "StarCasting.hpp" namespace Star { STAR_EXCEPTION(GuardedPtrInvalidAccess, StarException); STAR_EXCEPTION(BadGuardedPtrCast, StarException); // Simple guarded pointer class that, when the object that it points to is // destructed, automatically sets itself to invalid. Similar to // enable_shared_from_this, with the benefit of not requiring that the class be // managed by a shared_ptr. Accessing the guarded pointer is thread safe, with // one huge caveat: Because this model of guarded pointers relies on putting // no restrictions on object destruction, there is no thread safe way of // *locking* the pointer from destruction. The pattern of if (guardedPtr) { // guardedPtr->doThings(); } is inherently unsafe in a context where the // original object that guardedPtr points to could be deconstructed at any // time. If you need thread safety from destruction, then the only viable way // to do this is enable_shared_from_this and weak_ptr. template class GuardedPtr { public: typedef typename std::decay::type Type; typedef Type* PtrType; typedef Type& RefType; GuardedPtr(); GuardedPtr(PtrType ptr, shared_ptr> sharedValid); template GuardedPtr(GuardedPtr const& other); template GuardedPtr(GuardedPtr&& other); template GuardedPtr& operator=(GuardedPtr const& other); template GuardedPtr& operator=(GuardedPtr&& other); bool valid() const; explicit operator bool() const; PtrType get() const; template GuardedPtr as() const; template GuardedPtr convert() const; void reset(); PtrType operator->() const; RefType operator*() const; bool operator==(GuardedPtr const& ptr) const; bool operator!=(GuardedPtr const& ptr) const; bool operator==(PtrType ptr) const; bool operator!=(PtrType ptr) const; private: template friend class GuardedPtr; PtrType m_ptr; shared_ptr> m_sharedValid; }; // Holds a pointer and enables construction of GuardedPtr versions of that // pointer. If constructed with autoDelete, then the pointer will be // automatically deleted on destruction. template class GuardedHolder { public: typedef typename std::decay::type Type; typedef Type* PtrType; GuardedHolder(); GuardedHolder(PtrType ptr, bool autoDelete); ~GuardedHolder(); GuardedHolder(GuardedHolder const& rhs) = delete; GuardedHolder& operator=(GuardedHolder const& rhs) = delete; GuardedHolder(GuardedHolder&& guardedHolder); GuardedHolder& operator=(GuardedHolder&& guardedHolder); PtrType pointer() const; GuardedPtr guardedPtr() const; // Clears the held pointer and invalidates any GuardedPtrs that reference it. void clear(); // Clears the held pointer and invalidates any GuardedPtrs that reference it, // then updates the pointer value to a new value. void reset(PtrType ptr, bool autoDelete); private: bool m_autoDelete; PtrType m_ptr; shared_ptr> m_sharedValid; }; // Easy way to automatically support guardedPtrs to this. class GuardedHolderBase { public: GuardedHolderBase(); virtual ~GuardedHolderBase(); // Invalidates all existing GuardedPtrs to this, and resets the this holder // to 'this' again. void invalidateExistingGuardedPointers(); template friend GuardedPtr guardedPtr(T& t); template friend GuardedPtr guardedPtr(T const& t); template friend GuardedPtr guardedPtr(T* t); template friend GuardedPtr guardedPtr(T const* t); template friend GuardedPtr guardedPtr(shared_ptr t); template friend GuardedPtr guardedPtr(shared_ptr t); private: GuardedHolder m_thisHolder; }; template GuardedPtr as(GuardedPtr const& t); template GuardedPtr as(GuardedPtr const& t); template GuardedPtr::GuardedPtr() : m_ptr(nullptr) {} template GuardedPtr::GuardedPtr(PtrType ptr, shared_ptr> sharedValid) : m_ptr(move(ptr)), m_sharedValid(move(sharedValid)) {} template template GuardedPtr::GuardedPtr(GuardedPtr const& other) { m_ptr = other.m_ptr; m_sharedValid = other.m_sharedValid; } template template GuardedPtr::GuardedPtr(GuardedPtr&& other) { m_ptr = other.m_ptr; m_sharedValid = move(other.m_sharedValid); } template template GuardedPtr& GuardedPtr::operator=(GuardedPtr const& other) { m_ptr = other.m_ptr; m_sharedValid = other.m_sharedValid; return *this; } template template GuardedPtr& GuardedPtr::operator=(GuardedPtr&& other) { m_ptr = other.m_ptr; m_sharedValid = move(other.m_sharedValid); return *this; } template bool GuardedPtr::valid() const { return m_sharedValid && *m_sharedValid; } template GuardedPtr::operator bool() const { return valid(); } template auto GuardedPtr::get() const -> PtrType { if (valid()) return m_ptr; else return nullptr; } template template GuardedPtr GuardedPtr::convert() const { if (auto p = as()) return p; throw BadGuardedPtrCast(); } template template GuardedPtr GuardedPtr::as() const { if (valid()) { if (auto p = Star::as(m_ptr)) return GuardedPtr(p, m_sharedValid); } return {}; } template void GuardedPtr::reset() { m_sharedValid.reset(); } template auto GuardedPtr::operator->() const -> PtrType { if (auto p = get()) return p; else throw GuardedPtrInvalidAccess("Invalid access to invalid GuardedPtr"); } template auto GuardedPtr::operator*() const -> RefType { return *operator->(); } template bool GuardedPtr::operator==(GuardedPtr const& ptr) const { return get() == ptr.get(); } template bool GuardedPtr::operator!=(GuardedPtr const& ptr) const { return get() != ptr.get(); } template bool GuardedPtr::operator==(PtrType ptr) const { return get() == ptr; } template bool GuardedPtr::operator!=(PtrType ptr) const { return get() != ptr; } template GuardedHolder::GuardedHolder(PtrType ptr, bool autoDelete) : m_autoDelete(false), m_ptr(nullptr) { reset(ptr, autoDelete); } template GuardedHolder::~GuardedHolder() { clear(); } template GuardedHolder::GuardedHolder(GuardedHolder&& guardedHolder) { operator=(move(guardedHolder)); } template GuardedHolder& GuardedHolder::operator=(GuardedHolder&& guardedHolder) { clear(); m_autoDelete = move(guardedHolder.m_autoDelete); m_ptr = move(guardedHolder.m_ptr); m_sharedValid = move(guardedHolder.m_sharedValid); return *this; } template auto GuardedHolder::pointer() const -> PtrType { return m_ptr; } template auto GuardedHolder::guardedPtr() const -> GuardedPtr { return GuardedPtr(m_ptr, m_sharedValid); } template void GuardedHolder::clear() { reset(nullptr, false); } template void GuardedHolder::reset(PtrType ptr, bool autoDelete) { if (m_autoDelete && m_ptr) delete m_ptr; if (m_sharedValid) { *m_sharedValid = false; m_sharedValid.reset(); } m_autoDelete = autoDelete; m_ptr = ptr; if (m_ptr) m_sharedValid = make_shared>(true); } inline GuardedHolderBase::GuardedHolderBase() : m_thisHolder(this, false) {} inline GuardedHolderBase::~GuardedHolderBase() {} inline void GuardedHolderBase::invalidateExistingGuardedPointers() { m_thisHolder.reset(this, false); } template GuardedPtr guardedPtr(T& t) { return t.m_thisHolder.guardedPtr().template convert(); } template GuardedPtr guardedPtr(T const& t) { return t.m_thisHolder.guardedPtr().template convert(); } template GuardedPtr guardedPtr(T* t) { return t->m_thisHolder.guardedPtr().template convert(); } template GuardedPtr guardedPtr(T const* t) { return t->m_thisHolder.guardedPtr().template convert(); } template GuardedPtr guardedPtr(shared_ptr t) { return t->m_thisHolder.guardedPtr().template convert(); } template GuardedPtr guardedPtr(shared_ptr t) { return t->m_thisHolder.guardedPtr().template convert(); } } #endif