#ifndef PLATFORM_PTR_H_ #define PLATFORM_PTR_H_ #include #include #include #include "platform/impl/default/default_lock.h" #include "platform/logging.h" #include "platform/port/down_cast.h" namespace location { namespace nearby { namespace ptr_impl { class RefCount { public: RefCount() : lock_(), count_(kInitialCount) {} // Returns false if this operation doesn't make conceptual sense any more // (for example, if it leads to bringing count_ back from the dead). bool increment() { bool result; lock_.lock(); { // Avoid coming back from the dead. if (count_ < kInitialCount) { result = false; } else { count_++; result = true; } } lock_.unlock(); return result; } // Returns true if after this operation, count_ is 0. bool decrement() { bool result; lock_.lock(); { // It's alright for count_ to go negative because it will only be exactly // 0 once (since increment() makes sure that once you go negative, you // can't come back from the dead). count_--; result = (count_ == 0); } lock_.unlock(); return result; } private: static const std::int32_t kInitialCount; DefaultLock lock_; std::int32_t count_; }; } // namespace ptr_impl template class ObjectDestroyer { public: static void destroy(T* t) { delete t; } }; template class ArrayDestroyer { public: static void destroy(T* t) { delete[] t; } }; // Forward declarations to make it possible for Ptr (a class template) to // declare ConstifyPtr, DowncastPtr, and DowncastConstPtr (function templates) // as friends. // // Note that the default template parameters to Ptr need to be defined here (at // the first point of declaration), as opposed to at the actual definition of // Ptr (which is what one might reasonably expect). // // See https://isocpp.org/wiki/faq/templates#template-friends for more. template class Destroyer = ObjectDestroyer> class Ptr; template class ConstPtr; template ConstPtr ConstifyPtr(Ptr ptr); template Ptr DowncastPtr(Ptr base_ptr); template ConstPtr DowncastConstPtr(ConstPtr base_ptr); // A layer of indirection over a raw pointer, to buy flexibility in the // future to use, for instance: // // a) the in-built shared_ptr in modern implementations of C++, // b) a custom reference-counting mechanism, etc. // // , all without having to touch every line of our codebase that uses // pointers. // // Destroyer defines how the owned pointee should be destroyed, and is // expected to be a class template that provides at least a destroy() // method, like so: // // template // class MyDestroyer { // public: // static void destroy(T* t); // }; // // It defaults to ObjectDestroyer. template class Destroyer> class Ptr { public: // Provide an alias for use as a dependent name. typedef T PointeeType; Ptr() : pointee_(nullptr), ref_count_(nullptr) {} explicit Ptr(T* pointee, bool is_ref_counted = false, ptr_impl::RefCount* ref_count = nullptr) : pointee_(pointee), ref_count_( is_ref_counted ? (ref_count != nullptr ? ref_count : new ptr_impl::RefCount()) : nullptr) { init(); } Ptr(const Ptr& that) : pointee_(that.pointee_), ref_count_(that.ref_count_) { init(); } Ptr& operator=(const Ptr& other) { if (pointee_ != other.pointee_) { // If we're not currently ref-counted, then an assignment shouldn't lead // to any destruction of our past state -- that's the responsibility of // whichever instance of Ptr believes it owns pointee_. destroy(false); pointee_ = other.pointee_; ref_count_ = other.ref_count_; init(); } return *this; } // Conversion to Ptr, where T is trivially convertible to T2. E.g. // conversion from derived to base class. template operator Ptr() { return Ptr(pointee_, isRefCounted(), ref_count_); } ~Ptr() { if (isRefCounted()) { destroy(); } else { // Left empty on purpose. } } bool operator==(const Ptr& other) const { assert(!(this->isNull())); assert(!(other.isNull())); return ((*(this->pointee_) == *(other.pointee_)) && (this->isRefCounted() == other.isRefCounted())); } bool operator!=(const Ptr& other) const { return !(*this == other); } bool operator<(const Ptr& other) const { assert(!(this->isNull())); assert(!(other.isNull())); return *(this->pointee_) < *(other.pointee_); } // Calls Destroyer::destroy() to perform deallocation of pointee_. void destroy(bool should_destroy_if_not_ref_counted = true) { bool need_to_destroy = isRefCounted() ? ref_count_->decrement() : should_destroy_if_not_ref_counted; if (need_to_destroy) { delete ref_count_; Destroyer::destroy(pointee_); } ref_count_ = NULL; // NOLINT pointee_ = NULL; // NOLINT } // Use this function only when the ownership is held by someone else, and this // Ptr object has no responsibility to destroy it. void clear() { if (isRefCounted()) { NEARBY_LOG(FATAL, "Attempting to invoke clear() on a RefCounted Ptr."); } pointee_ = NULL; // NOLINT } T& operator*() const { assert(pointee_ != NULL); // NOLINT return *pointee_; } T* operator->() const { assert(pointee_ != NULL); // NOLINT return pointee_; } bool isNull() const { return pointee_ == nullptr; } bool isRefCounted() const { return ref_count_ != nullptr; } private: template friend ConstPtr ConstifyPtr(Ptr ptr); template friend Ptr DowncastPtr(Ptr base_ptr); template friend ConstPtr DowncastConstPtr(ConstPtr base_ptr); void init() { if (isRefCounted()) { if (!ref_count_->increment()) { NEARBY_LOG(FATAL, "Failed to increment RefCount."); } } } T* pointee_; ptr_impl::RefCount* ref_count_; }; // Convenience wrapper for a read-only version of Ptr (in which the pointee // cannot be modified). // // The C++11 equivalent would be: // // using ConstPtr = Ptr; // // Thus, // // Ptr x1(new X(...)); // // allows the underlying X instance to be modified, whereas // // ConstPtr x2(new X(...)); // // disallows that. template class ConstPtr : public Ptr { public: ConstPtr() {} explicit ConstPtr(T* pointee, bool is_ref_counted = false, ptr_impl::RefCount* ref_count = nullptr) : Ptr(pointee, is_ref_counted, ref_count) {} }; // RAII wrapper over Ptr and ConstPtr (hereon referred to by the PtrType // placeholder), to allow for guarantees that the wrapped PtrType will be // automatically destroyed when this wrapper object goes out of scope. // // Any class that has a PtrType member that it owns (and thus needs to invoke // destroy() on) should wrap that PtrType in a ScopedPtr object. // // Similarly, any method that manipulates a (likely local) PtrType variable // that needs to be destroy()ed at the end of that method should wrap that // PtrType variable in a ScopedPtr object. // // Sample usage: // // Ptr x1(new X(...)); // ScopedPtr > sx1(x1); // // ConstPtr x2(new X(...)); // ScopedPtr > sx2(x2); // // ScopedPtr > sx3(new X(...)); // // ScopedPtr > sx4(new X(...)); template class ScopedPtr { public: explicit ScopedPtr(typename PtrType::PointeeType* pointee) : ptr_(pointee) {} explicit ScopedPtr(PtrType ptr) : ptr_(ptr) {} ~ScopedPtr() { ptr_.destroy(); } // Shadow methods for the underlying Ptr. typename PtrType::PointeeType& operator*() const { return ptr_.operator*(); } typename PtrType::PointeeType* operator->() const { return ptr_.operator->(); } bool isNull() const { return ptr_.isNull(); } // Accessor for the underlying Ptr. PtrType get() const { return ptr_; } // Releases the underlying Ptr from the clutches of this ScopedPtr, // effectively resetting this ScopedPtr (and making its destructor be a no-op) // -- useful for transfer of ownership from one ScopedPtr to another across // scopes. PtrType release() { PtrType released = ptr_; ptr_ = PtrType(); return released; } private: // Disallow copy and assignment. ScopedPtr(const ScopedPtr&); ScopedPtr& operator=(const ScopedPtr&); PtrType ptr_; }; // Utility function to create Ptr objects with less template-y noise by // leveraging template argument deduction, in the same vein as std::make_pair(). // // Helps convert // // Ptr >(new MyRichType()); // // to // // MakePtr(new MyRichType()); template Ptr MakePtr(T* raw_ptr) { return Ptr(raw_ptr); } // Like MakePtr(), utility function to create ConstPtr objects with less // template-y noise. template ConstPtr MakeConstPtr(T* raw_ptr) { return ConstPtr(raw_ptr); } // Used to create Ptr instances that are reference-counted (for when the // lifetime and/or ownership of the pointee is not deterministic, like when a // cache gives out handles to its cached objects to multiple threads to manage // independently). // // Needless to say, the reference-counted-ness of these Ptr instances propagates // across all copies and assignments, and as one might expect, the underlying // pointee is deallocated when the reference count goes to 0. // // That implies that it's not strictly necessary to wrap these in ScopedPtrs // (but it's perfectly fine to do so, and is even recommended, so readers of // your code get a better understanding of the ownership story for each // reference). template Ptr MakeRefCountedPtr(T* raw_ptr) { return Ptr(raw_ptr, true); } // ConstPtr counterpart to MakeRefCountedPtr(). template ConstPtr MakeRefCountedConstPtr(T* raw_ptr) { return ConstPtr(raw_ptr, true); } // Use this function to convert a Ptr object to a ConstPtr object. template ConstPtr ConstifyPtr(Ptr ptr) { return ConstPtr(ptr.pointee_, ptr.isRefCounted(), ptr.ref_count_); } // Use this function to downcast from a Ptr to a Ptr. // // Because BaseT can be automatically deduced based on the base_ptr that's // passed in, invocations of this method only need to explicitly specify ChildT, // like so: // // Ptr my_child_ptr = DowncastPtr(my_base_ptr); template Ptr DowncastPtr(Ptr base_ptr) { return Ptr(DOWN_CAST(base_ptr.pointee_), base_ptr.isRefCounted(), base_ptr.ref_count_); } // ConstPtr counterpart to DowncastPtr(). template ConstPtr DowncastConstPtr(ConstPtr base_ptr) { return ConstPtr( const_cast(DOWN_CAST(base_ptr.pointee_)), base_ptr.isRefCounted(), base_ptr.ref_count_); } } // namespace nearby } // namespace location #endif // PLATFORM_PTR_H_