/** * @file llpointer.h * @brief A reference-counted pointer for objects derived from LLRefCount * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LLPOINTER_H #define LLPOINTER_H #include #include #include // std::swap() //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s // NOTE: LLPointer x = new LLFoo(); MAY NOT BE THREAD SAFE // if LLFoo::LLFoo() does anything like put itself in an update queue. // The queue may get accessed before it gets assigned to x. // The correct implementation is: // LLPointer x = new LLFoo; // constructor does not do anything interesting // x->instantiate(); // does stuff like place x into an update queue // see llthread.h for LLThreadSafeRefCount //---------------------------------------------------------------------------- class LLPointerBase { protected: // alert the coder that a referenced type's destructor did something very // strange -- this is in a non-template base class so we can hide the // implementation in llpointer.cpp static void wild_dtor(std::string_view msg); }; // Note: relies on Type having ref() and unref() methods template class LLPointer: public LLPointerBase { public: template friend class LLPointer; LLPointer() : mPointer(nullptr) { } LLPointer(Type* ptr) : mPointer(ptr) { ref(); } // Even though the template constructors below accepting // (const LLPointer&) and (LLPointer&&) appear to // subsume these specific (const LLPointer&) and (LLPointer&&) // constructors, the compiler recognizes these as The Copy Constructor and // The Move Constructor, respectively. In other words, even in the // presence of the LLPointer constructors, we still must specify // the LLPointer constructors. LLPointer(const LLPointer& ptr) : mPointer(ptr.mPointer) { ref(); } LLPointer(LLPointer&& ptr) noexcept { mPointer = ptr.mPointer; ptr.mPointer = nullptr; } // Support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. template LLPointer(const LLPointer& ptr) : mPointer(ptr.get()) { ref(); } template LLPointer(LLPointer&& ptr) noexcept : mPointer(ptr.get()) { ptr.mPointer = nullptr; } ~LLPointer() { unref(); } Type* get() const { return mPointer; } const Type* operator->() const { return mPointer; } Type* operator->() { return mPointer; } const Type& operator*() const { return *mPointer; } Type& operator*() { return *mPointer; } operator bool() const { return (mPointer != nullptr); } bool operator!() const { return (mPointer == nullptr); } bool isNull() const { return (mPointer == nullptr); } bool notNull() const { return (mPointer != nullptr); } operator Type*() const { return mPointer; } template bool operator !=(Type1* ptr) const { return (mPointer != ptr); } template bool operator ==(Type1* ptr) const { return (mPointer == ptr); } template bool operator !=(const LLPointer& ptr) const { return (mPointer != ptr.mPointer); } template bool operator ==(const LLPointer& ptr) const { return (mPointer == ptr.mPointer); } bool operator < (const LLPointer& ptr) const { return (mPointer < ptr.mPointer); } bool operator > (const LLPointer& ptr) const { return (mPointer > ptr.mPointer); } LLPointer& operator =(Type* ptr) { // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm LLPointer temp(ptr); using std::swap; // per Swappable convention swap(*this, temp); return *this; } // Even though the template assignment operators below accepting // (const LLPointer&) and (LLPointer&&) appear to // subsume these specific (const LLPointer&) and (LLPointer&&) // assignment operators, the compiler recognizes these as Copy Assignment // and Move Assignment, respectively. In other words, even in the presence // of the LLPointer assignment operators, we still must specify // the LLPointer operators. LLPointer& operator =(const LLPointer& ptr) { LLPointer temp(ptr); using std::swap; // per Swappable convention swap(*this, temp); return *this; } LLPointer& operator =(LLPointer&& ptr) { LLPointer temp(std::move(ptr)); using std::swap; // per Swappable convention swap(*this, temp); return *this; } // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. template LLPointer& operator =(const LLPointer& ptr) { LLPointer temp(ptr); using std::swap; // per Swappable convention swap(*this, temp); return *this; } template LLPointer& operator =(LLPointer&& ptr) { LLPointer temp(std::move(ptr)); using std::swap; // per Swappable convention swap(*this, temp); return *this; } // Just exchange the pointers, which will not change the reference counts. static void swap(LLPointer& a, LLPointer& b) { using std::swap; // per Swappable convention swap(a.mPointer, b.mPointer); } // Put swap() overload in the global namespace, per Swappable convention friend void swap(LLPointer& a, LLPointer& b) { LLPointer::swap(a, b); } protected: void ref() { if (mPointer) { mPointer->ref(); } } void unref() { if (mPointer) { Type *temp = mPointer; mPointer = nullptr; temp->unref(); if (mPointer != nullptr) { wild_dtor("Unreference did assignment to non-NULL because of destructor"); unref(); } } } protected: Type* mPointer; }; template using LLConstPointer = LLPointer; template class LLCopyOnWritePointer : public LLPointer { public: typedef LLCopyOnWritePointer self_t; typedef LLPointer pointer_t; LLCopyOnWritePointer() : mStayUnique(false) {} LLCopyOnWritePointer(Type* ptr) : LLPointer(ptr), mStayUnique(false) {} LLCopyOnWritePointer(LLPointer& ptr) : LLPointer(ptr), mStayUnique(false) { if (ptr.mStayUnique) { makeUnique(); } } Type* write() { makeUnique(); return pointer_t::mPointer; } void makeUnique() { if (pointer_t::notNull() && pointer_t::mPointer->getNumRefs() > 1) { *(pointer_t* )(this) = new Type(*pointer_t::mPointer); } } const Type* operator->() const { return pointer_t::mPointer; } const Type& operator*() const { return *pointer_t::mPointer; } void setStayUnique(bool stay) { makeUnique(); mStayUnique = stay; } private: bool mStayUnique; }; template bool operator!=(Type0* lhs, const LLPointer& rhs) { return (lhs != rhs.get()); } template bool operator==(Type0* lhs, const LLPointer& rhs) { return (lhs == rhs.get()); } // boost hash adapter template struct boost::hash> { typedef LLPointer argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const { return (std::size_t) s.get(); } }; // Adapt boost hash to std hash namespace std { template struct hash> { std::size_t operator()(LLPointer const& s) const noexcept { return boost::hash>()(s); } }; } #endif