// WARNING: Changes to this file must be contributed back to Sawyer or else they will // be clobbered by the next update from Sawyer. The Sawyer repository is at // https://github.com/matzke1/sawyer. #ifndef Sawyer_SharedPtr_H #define Sawyer_SharedPtr_H #include #include #include #include #include "Assert.h" #include "Optional.h" // FIXME[Robb Matzke 2014-08-22]: only needed for Sawyer::Nothing #include "Sawyer.h" #include "Synchronization.h" namespace Sawyer { /** Reference-counting smart pointer. * * This class is a reference-counting pointer to an object that inherits from @ref SharedObject. Usage is similar to * dyncompat::shared_ptr. * * @sa SharedObject, @ref SharedFromThis * * @todo Write documentation for SharedPointer. */ template class SharedPointer { public: typedef T Pointee; private: Pointee *pointee_; static void acquireOwnership(Pointee *rawPtr); // Returns number of owners remaining static size_t releaseOwnership(Pointee *rawPtr); public: /** Constructs an empty shared pointer. */ SharedPointer(): pointee_(NULL) {} /** Constructs a new pointer that shares ownership of the pointed-to object with the @p other pointer. The pointed-to * object will only be deleted after both pointers are deleted. * @{ */ SharedPointer(const SharedPointer &other): pointee_(other.pointee_) { acquireOwnership(pointee_); } template SharedPointer(const SharedPointer &other): pointee_(getRawPointer(other)) { acquireOwnership(pointee_); } /** @} */ /** Constructs a shared pointer for an object. * * If @p obj is non-null then its reference count is incremented. It is possible to create any number of shared pointers * to the same object using this constructor. The expression "delete obj" must be well formed and must not invoke * undefined behavior. */ template explicit SharedPointer(Y *rawPtr): pointee_(rawPtr) { if (pointee_!=NULL) acquireOwnership(pointee_); } /** Conditionally deletes the pointed-to object. The object is deleted when its reference count reaches zero. */ ~SharedPointer() { if (0==releaseOwnership(pointee_)) delete pointee_; } /** Assignment. This pointer is caused to point to the same object as @p other, decrementing the reference count for the * object originally pointed to by this pointer and incrementing the reference count for the object pointed by @p other. * @{ */ SharedPointer& operator=(const SharedPointer &other) { return operator=(other); } template SharedPointer& operator=(const SharedPointer &other) { if (pointee_!=getRawPointer(other)) { if (pointee_!=NULL && 0==releaseOwnership(pointee_)) delete pointee_; pointee_ = getRawPointer(other); acquireOwnership(pointee_); } return *this; } /** @} */ /** Assignment. This pointer is caused to point to nothing. */ SharedPointer& operator=(const Sawyer::Nothing&) { if (pointee_!=NULL && 0==releaseOwnership(pointee_)) delete pointee_; pointee_ = NULL; return *this; } /** Reference to the pointed-to object. An assertion will fail if assertions are enabled and this method is invoked on an * empty pointer. */ T& operator*() const { ASSERT_not_null2(pointee_, "shared pointer points to no object"); ASSERT_require(ownershipCount(pointee_)>0); return *pointee_; } /** Dereference pointed-to object. The pointed-to object is returned. Returns null for empty pointers. */ T* operator->() const { ASSERT_not_null2(pointee_, "shared pointer points to no object"); ASSERT_require(ownershipCount(pointee_)>0); return pointee_; } /** @} */ /** Dynamic cast. * * Casts the specified pointer to a new pointer type using dynamic_cast. */ template SharedPointer dynamicCast() const { return SharedPointer(dynamic_cast(pointee_)); } /** Comparison of two pointers. * * Comparisons operators compare the underlying pointers to objects. * * @{ */ template bool operator==(const SharedPointer &other) const { return pointee_ == getRawPointer(other); } template bool operator!=(const SharedPointer &other) const { return pointee_ != getRawPointer(other); } template bool operator<(const SharedPointer &other) const { return pointee_ < getRawPointer(other); } template bool operator<=(const SharedPointer &other) const { return pointee_ <= getRawPointer(other); } template bool operator>(const SharedPointer &other) const { return pointee_ > getRawPointer(other); } template bool operator>=(const SharedPointer &other) const { return pointee_ >= getRawPointer(other); } /** Comparison of two pointers. * * Compares the underlying pointer with the specified pointer. * * @{ */ template bool operator==(const U *ptr) const { return pointee_ == ptr; } template bool operator!=(const U *ptr) const { return pointee_ != ptr; } template bool operator<(const U *ptr) const { return pointee_ < ptr; } template bool operator<=(const U *ptr) const { return pointee_ <= ptr; } template bool operator>(const U *ptr) const { return pointee_ > ptr; } template bool operator>=(const U *ptr) const { return pointee_ >= ptr; } /** @} */ /** Boolean complement. * * This operator allows shared pointers to be used in situations like this: * * @code * SharedPointer obj = ...; * if (!obj) ... * @endcode */ bool operator!() const { return pointee_==NULL; } /** Print a shared pointer. * * Printing a shared pointer is the same as printing the pointee's address. */ friend std::ostream& operator<<(std::ostream &out, const SharedPointer &ptr) { out <if or while * statement. For instance: * * @code * SharedPointer x = ...; * if (x) { * //this is reached * } * @endcode */ operator unspecified_bool() const { return pointee_ ? &SharedPointer::this_type_does_not_support_comparisons : 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Functions // These are functions because we don't want to add any pointer methods that could be confused with methods on the // pointee. For instance, if the pointee has an "ownershipCount" method then errors could be easily be introduced. For // instance, these two lines both compile but do entirely different things: // object->ownershipCount() // object.ownershipCount(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Obtain the pointed-to object. * * The pointed-to object is returned. Returns null for empty pointers. An idiom for getting a raw pointer for a shared * pointer that's known to be non-null is to dereference the shared pointer and then take the address: * * @code * SharedPointer ptr = ...; * MyType *obj = &*ptr; * @endcode */ friend Pointee* getRawPointer(const SharedPointer &ptr) { return ptr.pointee_; } /** Returns the pointed-to object's reference count. Returns zero for empty pointers. */ friend size_t ownershipCount(const SharedPointer &ptr) { return ptr.ownershipCount(ptr.pointee_); } private: static size_t ownershipCount(Pointee *rawPtr); }; /** Make pointer point to nothing. * * Clears the pointer so it points to no object. A cleared pointer is like a default-constructed pointer. */ template void clear(SharedPointer &ptr) { ptr = SharedPointer(); } /** Base class for reference counted objects. * * Any reference counted object should inherit from this class, which provides a default constructor, virtual destructor, and * a private reference count data member. * * @sa SharedPointer, @ref SharedFromThis */ class SAWYER_EXPORT SharedObject { template friend class SharedPointer; mutable SAWYER_THREAD_TRAITS::Mutex mutex_; mutable size_t nrefs_; public: /** Default constructor. Initializes the reference count to zero. */ SharedObject(): nrefs_(0) {} /** Copy constructor. * * The reference count of the new object is set to zero since no pointers can exist yet. */ SharedObject(const SharedObject &/*other*/): nrefs_(0) {} /** Assignment. * * Assigning one object to another doesn't change the reference count or mutex of either object. */ SharedObject& operator=(const SharedObject &/*other*/) { return *this; } /** Virtual destructor. Verifies that the reference count is zero. */ virtual ~SharedObject() { ASSERT_require(nrefs_==0); } }; /** Creates SharedPointer from this. * * This class provides a @ref sharedFromThis method that returns a @ref SharedPointer pointing to an object of type @c T. * The template parameter @c T is usually the name of the class derived from SharedFromThis. For instance, the following * example declares @c MyBaseClass to be a shared object (able to be pointed to by a SharedPointer), and declares that it * is possible to create a SharedPointer from a raw object pointer. * * @code * class MyBaseClass: public SharedObject, public SharedFromThis { ... }; * @endcode * * Some method in @c MyBaseClass might want to return such a pointer: * * @code * SharedPointer update() const { * ... * return sharedFromThis(); * } * @endcode * * Subclasses in a class hierarchy need not all have the same version of @c sharedFromThis. If the user's base class is * the only one to directly derive from SharedFromThis, then any subclass calling @c sharedFromThis will return a pointer to * the user's base class. On the other hand, the various subclasses can directly inherit from SharedFromThis also in order to * return pointers to objects of their type: * * @code * class MyDerivedA: public MyBaseClass { * // sharedFromThis() returns pointers to MyBaseClass * }; * * class MyDerivedB: public MyBaseClass, public SharedFromThis { * // sharedFromThis() is overridden to return pointers to MyDerivedB * } * @endcode */ template class SharedFromThis { public: virtual ~SharedFromThis() {} /** Create a shared pointer from this. * * Returns a shared pointer that points to this object. The type @c T must be derived from SharedObject. * * @{ */ SharedPointer sharedFromThis() { T *derived = dynamic_cast(this); ASSERT_not_null(derived); return SharedPointer(derived); } SharedPointer sharedFromThis() const { const T *derived = dynamic_cast(this); ASSERT_not_null(derived); return SharedPointer(derived); } /** @} */ }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Implementations //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DYNINST_DIAGNOSTIC_BEGIN_SUPPRESS_UNUSED_VARIABLE template inline size_t SharedPointer::ownershipCount(T *rawPtr) { if (rawPtr) { SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_); return rawPtr->SharedObject::nrefs_; } return 0; } template inline void SharedPointer::acquireOwnership(Pointee *rawPtr) { if (rawPtr!=NULL) { SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_); ++rawPtr->SharedObject::nrefs_; } } template inline size_t SharedPointer::releaseOwnership(Pointee *rawPtr) { if (rawPtr!=NULL) { SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_); assert(rawPtr->SharedObject::nrefs_ > 0); return --rawPtr->SharedObject::nrefs_; } else { return 0; } } DYNINST_DIAGNOSTIC_END_SUPPRESS_UNUSED_VARIABLE } // namespace #endif