// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef PLATFORM_PTR_H_ #define PLATFORM_PTR_H_ #include #include #include #include #include #include "platform/logging.h" #include "platform/port/down_cast.h" namespace location { namespace nearby { // 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 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. // It is being deprecated in favor of standard c++ smart pointers. // For transion period, Ptr will behave similar to shared_ptr. // New code should use shrared_ptr or unique_ptr and not Ptr. template class Ptr { public: // Provide an alias for use as a dependent name. typedef T PointeeType; Ptr() = default; explicit Ptr(T* pointee) : ptr_(pointee) {} Ptr(const Ptr& that) = default; Ptr(std::shared_ptr ptr) : ptr_(ptr) {} // NOLINT template Ptr& operator=(T2* ptr) { Ptr tmp(ptr); this->ptr_.swap(tmp); return *this; } Ptr& operator=(const Ptr& other) = default; // Conversion to Ptr, where T is trivially convertible to T2. E.g. // conversion from derived to base class. template operator Ptr() { // NOLINT return Ptr(std::static_pointer_cast(this->ptr_)); } operator Ptr() { // NOLINT return Ptr(*this); } explicit operator std::shared_ptr() { return this->ptr_; } ~Ptr() = default; bool operator==(const Ptr& other) const { return *(this->ptr_) == *(other.ptr_); } bool operator!=(const Ptr& other) const { return !(*this == other); } bool operator<(const Ptr& other) const { return *(this->ptr_) < *(other.ptr_); } // No-op: refcounted objects will be destroyed correctly ABSL_DEPRECATED("Use c++ smart pointers directly instead of Ptr") void destroy(bool = true) {} // No-op: refcounted objects will be destroyed correctly ABSL_DEPRECATED("Use c++ smart pointers directly instead of Ptr") void clear() {} T& operator*() const { return *ptr_; } T* operator->() const { return ptr_.get(); } T* get() { return ptr_.get(); } void reset() { return ptr_.reset(); } ABSL_DEPRECATED("Use c++ smart pointers directly instead of Ptr") bool isNull() const { return !this->ptr_; } // used by pipe.cc; introduced by cr/295271652 ABSL_DEPRECATED("Use c++ smart pointers directly instead of Ptr") bool isRefCounted() const { return true; } private: template friend ConstPtr ConstifyPtr(Ptr ptr); template friend Ptr DowncastPtr(Ptr base_ptr); template friend ConstPtr DowncastConstPtr(ConstPtr base_ptr); std::shared_ptr ptr_; }; // 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(const T* pointee) : Ptr(pointee) {} explicit ConstPtr(T* pointee) : Ptr(pointee) {} explicit ConstPtr(Ptr ptr) : Ptr(ptr) {} }; // 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(const ScopedPtr&) = delete; ~ScopedPtr() = default; ScopedPtr& operator=(const ScopedPtr&) = delete; // Shadow methods for the underlying Ptr. typename PtrType::PointeeType& operator*() const { return *ptr_; } typename PtrType::PointeeType* operator->() const { return ptr_.operator->(); } bool isNull() const { return ptr_.isNull(); } // Accessor for the underlying Ptr. PtrType get() const { return this->ptr_; } // Does nothing; // this is to avoid unintended destruction of a managed pointer. // TODO(b/149938110): remove this completely. PtrType release() { return ptr_; } private: 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); } // ConstPtr counterpart to MakeRefCountedPtr(). template ConstPtr MakeRefCountedConstPtr(T* raw_ptr) { return ConstPtr(raw_ptr); } // Use this function to convert a Ptr object to a ConstPtr object. template ConstPtr ConstifyPtr(Ptr ptr) { return ConstPtr(ptr); } // 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) { static_assert(std::is_base_of_v); return Ptr(std::static_pointer_cast(base_ptr.ptr_)); } // ConstPtr counterpart to DowncastPtr(). template ConstPtr DowncastConstPtr(ConstPtr base_ptr) { static_assert(std::is_base_of_v); return ConstPtr( std::static_pointer_cast(base_ptr.ptr_)); } } // namespace nearby } // namespace location #endif // PLATFORM_PTR_H_