michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsAutoRef_h_ michael@0: #define nsAutoRef_h_ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "nscore.h" // for nullptr, bool michael@0: michael@0: template class nsSimpleRef; michael@0: template class nsAutoRefBase; michael@0: template class nsReturnRef; michael@0: template class nsReturningRef; michael@0: michael@0: /** michael@0: * template class nsAutoRef michael@0: * michael@0: * A class that holds a handle to a resource that must be released. michael@0: * No reference is added on construction. michael@0: * michael@0: * No copy constructor nor copy assignment operators are available, so the michael@0: * resource will be held until released on destruction or explicitly michael@0: * |reset()| or transferred through provided methods. michael@0: * michael@0: * The publicly available methods are the public methods on this class and its michael@0: * public base classes |nsAutoRefBase| and |nsSimpleRef|. michael@0: * michael@0: * For ref-counted resources see also |nsCountedRef|. michael@0: * For function return values see |nsReturnRef|. michael@0: * michael@0: * For each class |T|, |nsAutoRefTraits| or |nsSimpleRef| must be michael@0: * specialized to use |nsAutoRef| and |nsCountedRef|. michael@0: * michael@0: * @param T A class identifying the type of reference held by the michael@0: * |nsAutoRef| and the unique set methods for managing references michael@0: * to the resource (defined by |nsAutoRefTraits| or michael@0: * |nsSimpleRef|). michael@0: * michael@0: * Often this is the class representing the resource. Sometimes a michael@0: * new possibly-incomplete class may need to be declared. michael@0: * michael@0: * michael@0: * Example: An Automatically closing file descriptor michael@0: * michael@0: * // References that are simple integral types (as file-descriptors are) michael@0: * // usually need a new class to represent the resource and how to handle its michael@0: * // references. michael@0: * class nsRawFD; michael@0: * michael@0: * // Specializing nsAutoRefTraits describes how to manage file michael@0: * // descriptors, so that nsAutoRef provides automatic closing of michael@0: * // its file descriptor on destruction. michael@0: * template <> michael@0: * class nsAutoRefTraits { michael@0: * public: michael@0: * // The file descriptor is held in an int. michael@0: * typedef int RawRef; michael@0: * // -1 means that there is no file associated with the handle. michael@0: * static int Void() { return -1; } michael@0: * // The file associated with a file descriptor is released with close(). michael@0: * static void Release(RawRef aFD) { close(aFD); } michael@0: * }; michael@0: * michael@0: * // A function returning a file descriptor that must be closed. michael@0: * nsReturnRef get_file(const char *filename) { michael@0: * // Constructing from a raw file descriptor assumes ownership. michael@0: * nsAutoRef fd(open(filename, O_RDONLY)); michael@0: * fcntl(fd, F_SETFD, FD_CLOEXEC); michael@0: * return fd.out(); michael@0: * } michael@0: * michael@0: * void f() { michael@0: * unsigned char buf[1024]; michael@0: * michael@0: * // Hold a file descriptor for /etc/hosts in fd1. michael@0: * nsAutoRef fd1(get_file("/etc/hosts")); michael@0: * michael@0: * nsAutoRef fd2; michael@0: * fd2.steal(fd1); // fd2 takes the file descriptor from fd1 michael@0: * ssize_t count = read(fd1, buf, 1024); // error fd1 has no file michael@0: * count = read(fd2, buf, 1024); // reads from /etc/hosts michael@0: * michael@0: * // If the file descriptor is not stored then it is closed. michael@0: * get_file("/etc/login.defs"); // login.defs is closed michael@0: * michael@0: * // Now use fd1 to hold a file descriptor for /etc/passwd. michael@0: * fd1 = get_file("/etc/passwd"); michael@0: * michael@0: * // The nsAutoRef can give up the file descriptor if explicitly michael@0: * // instructed, but the caller must then ensure that the file is closed. michael@0: * int rawfd = fd1.disown(); michael@0: * michael@0: * // Assume ownership of another file descriptor. michael@0: * fd1.own(open("/proc/1/maps"); michael@0: * michael@0: * // On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts, michael@0: * // but /etc/passwd is not closed. michael@0: * } michael@0: * michael@0: */ michael@0: michael@0: michael@0: template michael@0: class nsAutoRef : public nsAutoRefBase michael@0: { michael@0: protected: michael@0: typedef nsAutoRef ThisClass; michael@0: typedef nsAutoRefBase BaseClass; michael@0: typedef nsSimpleRef SimpleRef; michael@0: typedef typename BaseClass::RawRefOnly RawRefOnly; michael@0: typedef typename BaseClass::LocalSimpleRef LocalSimpleRef; michael@0: michael@0: public: michael@0: nsAutoRef() michael@0: { michael@0: } michael@0: michael@0: // Explicit construction is required so as not to risk unintentionally michael@0: // releasing the resource associated with a raw ref. michael@0: explicit nsAutoRef(RawRefOnly aRefToRelease) michael@0: : BaseClass(aRefToRelease) michael@0: { michael@0: } michael@0: michael@0: // Construction from a nsReturnRef function return value, which expects michael@0: // to give up ownership, transfers ownership. michael@0: // (nsReturnRef is converted to const nsReturningRef.) michael@0: explicit nsAutoRef(const nsReturningRef& aReturning) michael@0: : BaseClass(aReturning) michael@0: { michael@0: } michael@0: michael@0: // The only assignment operator provided is for transferring from an michael@0: // nsReturnRef smart reference, which expects to pass its ownership to michael@0: // another object. michael@0: // michael@0: // With raw references and other smart references, the type of the lhs and michael@0: // its taking and releasing nature is often not obvious from an assignment michael@0: // statement. Assignment from a raw ptr especially is not normally michael@0: // expected to release the reference. michael@0: // michael@0: // Use |steal| for taking ownership from other smart refs. michael@0: // michael@0: // For raw references, use |own| to indicate intention to have the michael@0: // resource released. michael@0: // michael@0: // Or, to create another owner of the same reference, use an nsCountedRef. michael@0: michael@0: ThisClass& operator=(const nsReturningRef& aReturning) michael@0: { michael@0: BaseClass::steal(aReturning.mReturnRef); michael@0: return *this; michael@0: } michael@0: michael@0: // Conversion to a raw reference allow the nsAutoRef to often be used michael@0: // like a raw reference. michael@0: operator typename SimpleRef::RawRef() const michael@0: { michael@0: return this->get(); michael@0: } michael@0: michael@0: // Transfer ownership from another smart reference. michael@0: void steal(ThisClass& aOtherRef) michael@0: { michael@0: BaseClass::steal(aOtherRef); michael@0: } michael@0: michael@0: // Assume ownership of a raw ref. michael@0: // michael@0: // |own| has similar function to |steal|, and is useful for receiving michael@0: // ownership from a return value of a function. It is named differently michael@0: // because |own| requires more care to ensure that the function intends to michael@0: // give away ownership, and so that |steal| can be safely used, knowing michael@0: // that it won't steal ownership from any methods returning raw ptrs to michael@0: // data owned by a foreign object. michael@0: void own(RawRefOnly aRefToRelease) michael@0: { michael@0: BaseClass::own(aRefToRelease); michael@0: } michael@0: michael@0: // Exchange ownership with |aOther| michael@0: void swap(ThisClass& aOther) michael@0: { michael@0: LocalSimpleRef temp; michael@0: temp.SimpleRef::operator=(*this); michael@0: SimpleRef::operator=(aOther); michael@0: aOther.SimpleRef::operator=(temp); michael@0: } michael@0: michael@0: // Release the reference now. michael@0: void reset() michael@0: { michael@0: this->SafeRelease(); michael@0: LocalSimpleRef empty; michael@0: SimpleRef::operator=(empty); michael@0: } michael@0: michael@0: // Pass out the reference for a function return values. michael@0: nsReturnRef out() michael@0: { michael@0: return nsReturnRef(this->disown()); michael@0: } michael@0: michael@0: // operator->() and disown() are provided by nsAutoRefBase. michael@0: // The default nsSimpleRef provides get(). michael@0: michael@0: private: michael@0: // No copy constructor michael@0: explicit nsAutoRef(ThisClass& aRefToSteal); michael@0: }; michael@0: michael@0: /** michael@0: * template class nsCountedRef michael@0: * michael@0: * A class that creates (adds) a new reference to a resource on construction michael@0: * or assignment and releases on destruction. michael@0: * michael@0: * This class is similar to nsAutoRef and inherits its methods, but also michael@0: * provides copy construction and assignment operators that enable more than michael@0: * one concurrent reference to the same resource. michael@0: * michael@0: * Specialize |nsAutoRefTraits| or |nsSimpleRef| to use this. This michael@0: * class assumes that the resource itself counts references and so can only be michael@0: * used when |T| represents a reference-counting resource. michael@0: */ michael@0: michael@0: template michael@0: class nsCountedRef : public nsAutoRef michael@0: { michael@0: protected: michael@0: typedef nsCountedRef ThisClass; michael@0: typedef nsAutoRef BaseClass; michael@0: typedef nsSimpleRef SimpleRef; michael@0: typedef typename BaseClass::RawRef RawRef; michael@0: michael@0: public: michael@0: nsCountedRef() michael@0: { michael@0: } michael@0: michael@0: // Construction and assignment from a another nsCountedRef michael@0: // or a raw ref copies and increments the ref count. michael@0: nsCountedRef(const ThisClass& aRefToCopy) michael@0: { michael@0: SimpleRef::operator=(aRefToCopy); michael@0: SafeAddRef(); michael@0: } michael@0: ThisClass& operator=(const ThisClass& aRefToCopy) michael@0: { michael@0: if (this == &aRefToCopy) michael@0: return *this; michael@0: michael@0: this->SafeRelease(); michael@0: SimpleRef::operator=(aRefToCopy); michael@0: SafeAddRef(); michael@0: return *this; michael@0: } michael@0: michael@0: // Implicit conversion from another smart ref argument (to a raw ref) is michael@0: // accepted here because construction and assignment safely creates a new michael@0: // reference without interfering with the reference to copy. michael@0: explicit nsCountedRef(RawRef aRefToCopy) michael@0: : BaseClass(aRefToCopy) michael@0: { michael@0: SafeAddRef(); michael@0: } michael@0: ThisClass& operator=(RawRef aRefToCopy) michael@0: { michael@0: this->own(aRefToCopy); michael@0: SafeAddRef(); michael@0: return *this; michael@0: } michael@0: michael@0: // Construction and assignment from an nsReturnRef function return value, michael@0: // which expects to give up ownership, transfers ownership. michael@0: explicit nsCountedRef(const nsReturningRef& aReturning) michael@0: : BaseClass(aReturning) michael@0: { michael@0: } michael@0: ThisClass& operator=(const nsReturningRef& aReturning) michael@0: { michael@0: BaseClass::operator=(aReturning); michael@0: return *this; michael@0: } michael@0: michael@0: protected: michael@0: // Increase the reference count if there is a resource. michael@0: void SafeAddRef() michael@0: { michael@0: if (this->HaveResource()) michael@0: this->AddRef(this->get()); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * template class nsReturnRef michael@0: * michael@0: * A type for function return values that hold a reference to a resource that michael@0: * must be released. See also |nsAutoRef::out()|. michael@0: */ michael@0: michael@0: template michael@0: class nsReturnRef : public nsAutoRefBase michael@0: { michael@0: protected: michael@0: typedef nsAutoRefBase BaseClass; michael@0: typedef typename BaseClass::RawRefOnly RawRefOnly; michael@0: michael@0: public: michael@0: // For constructing a return value with no resource michael@0: nsReturnRef() michael@0: { michael@0: } michael@0: michael@0: // For returning a smart reference from a raw reference that must be michael@0: // released. Explicit construction is required so as not to risk michael@0: // unintentionally releasing the resource associated with a raw ref. michael@0: explicit nsReturnRef(RawRefOnly aRefToRelease) michael@0: : BaseClass(aRefToRelease) michael@0: { michael@0: } michael@0: michael@0: // Copy construction transfers ownership michael@0: nsReturnRef(nsReturnRef& aRefToSteal) michael@0: : BaseClass(aRefToSteal) michael@0: { michael@0: } michael@0: michael@0: nsReturnRef(const nsReturningRef& aReturning) michael@0: : BaseClass(aReturning) michael@0: { michael@0: } michael@0: michael@0: // Conversion to a temporary (const) object referring to this object so michael@0: // that the reference may be passed from a function return value michael@0: // (temporary) to another smart reference. There is no need to use this michael@0: // explicitly. Simply assign a nsReturnRef function return value to a michael@0: // smart reference. michael@0: operator nsReturningRef() michael@0: { michael@0: return nsReturningRef(*this); michael@0: } michael@0: michael@0: // No conversion to RawRef operator is provided on nsReturnRef, to ensure michael@0: // that the return value is not carelessly assigned to a raw ptr (and the michael@0: // resource then released). If passing to a function that takes a raw michael@0: // ptr, use get or disown as appropriate. michael@0: }; michael@0: michael@0: /** michael@0: * template class nsReturningRef michael@0: * michael@0: * A class to allow ownership to be transferred from nsReturnRef function michael@0: * return values. michael@0: * michael@0: * It should not be necessary for clients to reference this michael@0: * class directly. Simply pass an nsReturnRef to a parameter taking an michael@0: * |nsReturningRef|. michael@0: * michael@0: * The conversion operator on nsReturnRef constructs a temporary wrapper of michael@0: * class nsReturningRef around a non-const reference to the nsReturnRef. michael@0: * The wrapper can then be passed as an rvalue parameter. michael@0: */ michael@0: michael@0: template michael@0: class nsReturningRef michael@0: { michael@0: private: michael@0: friend class nsReturnRef; michael@0: michael@0: explicit nsReturningRef(nsReturnRef& aReturnRef) michael@0: : mReturnRef(aReturnRef) michael@0: { michael@0: } michael@0: public: michael@0: nsReturnRef& mReturnRef; michael@0: }; michael@0: michael@0: /** michael@0: * template class nsAutoRefTraits michael@0: * michael@0: * A class describing traits of references managed by the default michael@0: * |nsSimpleRef| implementation and thus |nsAutoRef| and |nsCountedRef|. michael@0: * The default |nsSimpleRef is suitable for resources with handles that michael@0: * have a void value. (If there is no such void value for a handle, michael@0: * specialize |nsSimpleRef|.) michael@0: * michael@0: * Specializations must be provided for each class |T| according to the michael@0: * following pattern: michael@0: * michael@0: * // The template parameter |T| should be a class such that the set of fields michael@0: * // in class nsAutoRefTraits is unique for class |T|. Usually the michael@0: * // resource object class is sufficient. For handles that are simple michael@0: * // integral typedefs, a new unique possibly-incomplete class may need to be michael@0: * // declared. michael@0: * michael@0: * template <> michael@0: * class nsAutoRefTraits michael@0: * { michael@0: * // Specializations must provide a typedef for RawRef, describing the michael@0: * // type of the handle to the resource. michael@0: * typedef RawRef; michael@0: * michael@0: * // Specializations should define Void(), a function returning a value michael@0: * // suitable for a handle that does not have an associated resource. michael@0: * // michael@0: * // The return type must be a suitable as the parameter to a RawRef michael@0: * // constructor and operator==. michael@0: * // michael@0: * // If this method is not accessible then some limited nsAutoRef michael@0: * // functionality will still be available, but the default constructor, michael@0: * // |reset|, and most transfer of ownership methods will not be available. michael@0: * static Void(); michael@0: * michael@0: * // Specializations must define Release() to properly finalize the michael@0: * // handle to a non-void custom-deleted or reference-counted resource. michael@0: * static void Release(RawRef aRawRef); michael@0: * michael@0: * // For reference-counted resources, if |nsCountedRef| is to be used, michael@0: * // specializations must define AddRef to increment the reference count michael@0: * // held by a non-void handle. michael@0: * // (AddRef() is not necessary for |nsAutoRef|.) michael@0: * static void AddRef(RawRef aRawRef); michael@0: * }; michael@0: * michael@0: * See nsPointerRefTraits for example specializations for simple pointer michael@0: * references. See nsAutoRef for an example specialization for a non-pointer michael@0: * reference. michael@0: */ michael@0: michael@0: template class nsAutoRefTraits; michael@0: michael@0: /** michael@0: * template class nsPointerRefTraits michael@0: * michael@0: * A convenience class useful as a base class for specializations of michael@0: * |nsAutoRefTraits| where the handle to the resource is a pointer to |T|. michael@0: * By inheriting from this class, definitions of only Release(RawRef) and michael@0: * possibly AddRef(RawRef) need to be added. michael@0: * michael@0: * Examples of use: michael@0: * michael@0: * template <> michael@0: * class nsAutoRefTraits : public nsPointerRefTraits michael@0: * { michael@0: * public: michael@0: * static void Release(PRFileDesc *ptr) { PR_Close(ptr); } michael@0: * }; michael@0: * michael@0: * template <> michael@0: * class nsAutoRefTraits : public nsPointerRefTraits michael@0: * { michael@0: * public: michael@0: * static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); } michael@0: * static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); } michael@0: * }; michael@0: */ michael@0: michael@0: template michael@0: class nsPointerRefTraits michael@0: { michael@0: public: michael@0: // The handle is a pointer to T. michael@0: typedef T* RawRef; michael@0: // A nullptr does not have a resource. michael@0: static RawRef Void() { return nullptr; } michael@0: }; michael@0: michael@0: /** michael@0: * template class nsSimpleRef michael@0: * michael@0: * Constructs a non-smart reference, and provides methods to test whether michael@0: * there is an associated resource and (if so) get its raw handle. michael@0: * michael@0: * A default implementation is suitable for resources with handles that have a michael@0: * void value. This is not intended for direct use but used by |nsAutoRef| michael@0: * and thus |nsCountedRef|. michael@0: * michael@0: * Specialize this class if there is no particular void value for the resource michael@0: * handle. A specialized implementation must also provide Release(RawRef), michael@0: * and, if |nsCountedRef| is required, AddRef(RawRef), as described in michael@0: * nsAutoRefTraits. michael@0: */ michael@0: michael@0: template michael@0: class nsSimpleRef : protected nsAutoRefTraits michael@0: { michael@0: protected: michael@0: // The default implementation uses nsAutoRefTrait. michael@0: // Specializations need not define this typedef. michael@0: typedef nsAutoRefTraits Traits; michael@0: // The type of the handle to the resource. michael@0: // A specialization must provide a typedef for RawRef. michael@0: typedef typename Traits::RawRef RawRef; michael@0: michael@0: // Construct with no resource. michael@0: // michael@0: // If this constructor is not accessible then some limited nsAutoRef michael@0: // functionality will still be available, but the default constructor, michael@0: // |reset|, and most transfer of ownership methods will not be available. michael@0: nsSimpleRef() michael@0: : mRawRef(Traits::Void()) michael@0: { michael@0: } michael@0: // Construct with a handle to a resource. michael@0: // A specialization must provide this. michael@0: nsSimpleRef(RawRef aRawRef) michael@0: : mRawRef(aRawRef) michael@0: { michael@0: } michael@0: michael@0: // Test whether there is an associated resource. A specialization must michael@0: // provide this. The function is permitted to always return true if the michael@0: // default constructor is not accessible, or if Release (and AddRef) can michael@0: // deal with void handles. michael@0: bool HaveResource() const michael@0: { michael@0: return mRawRef != Traits::Void(); michael@0: } michael@0: michael@0: public: michael@0: // A specialization must provide get() or loose some functionality. This michael@0: // is inherited by derived classes and the specialization may choose michael@0: // whether it is public or protected. michael@0: RawRef get() const michael@0: { michael@0: return mRawRef; michael@0: } michael@0: michael@0: private: michael@0: RawRef mRawRef; michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * template class nsAutoRefBase michael@0: * michael@0: * Internal base class for |nsAutoRef| and |nsReturnRef|. michael@0: * Adds release on destruction to a |nsSimpleRef|. michael@0: */ michael@0: michael@0: template michael@0: class nsAutoRefBase : public nsSimpleRef michael@0: { michael@0: protected: michael@0: typedef nsAutoRefBase ThisClass; michael@0: typedef nsSimpleRef SimpleRef; michael@0: typedef typename SimpleRef::RawRef RawRef; michael@0: michael@0: nsAutoRefBase() michael@0: { michael@0: } michael@0: michael@0: // A type for parameters that should be passed a raw ref but should not michael@0: // accept implicit conversions (from another smart ref). (The only michael@0: // conversion to this type is from a raw ref so only raw refs will be michael@0: // accepted.) michael@0: class RawRefOnly michael@0: { michael@0: public: michael@0: RawRefOnly(RawRef aRawRef) michael@0: : mRawRef(aRawRef) michael@0: { michael@0: } michael@0: operator RawRef() const michael@0: { michael@0: return mRawRef; michael@0: } michael@0: private: michael@0: RawRef mRawRef; michael@0: }; michael@0: michael@0: // Construction from a raw ref assumes ownership michael@0: explicit nsAutoRefBase(RawRefOnly aRefToRelease) michael@0: : SimpleRef(aRefToRelease) michael@0: { michael@0: } michael@0: michael@0: // Constructors that steal ownership michael@0: explicit nsAutoRefBase(ThisClass& aRefToSteal) michael@0: : SimpleRef(aRefToSteal.disown()) michael@0: { michael@0: } michael@0: explicit nsAutoRefBase(const nsReturningRef& aReturning) michael@0: : SimpleRef(aReturning.mReturnRef.disown()) michael@0: { michael@0: } michael@0: michael@0: ~nsAutoRefBase() michael@0: { michael@0: SafeRelease(); michael@0: } michael@0: michael@0: // An internal class providing access to protected nsSimpleRef michael@0: // constructors for construction of temporary simple references (that are michael@0: // not ThisClass). michael@0: class LocalSimpleRef : public SimpleRef michael@0: { michael@0: public: michael@0: LocalSimpleRef() michael@0: { michael@0: } michael@0: explicit LocalSimpleRef(RawRef aRawRef) michael@0: : SimpleRef(aRawRef) michael@0: { michael@0: } michael@0: }; michael@0: michael@0: private: michael@0: ThisClass& operator=(const ThisClass& aSmartRef) MOZ_DELETE; michael@0: michael@0: public: michael@0: RawRef operator->() const michael@0: { michael@0: return this->get(); michael@0: } michael@0: michael@0: // Transfer ownership to a raw reference. michael@0: // michael@0: // THE CALLER MUST ENSURE THAT THE REFERENCE IS EXPLICITLY RELEASED. michael@0: // michael@0: // Is this really what you want to use? Using this removes any guarantee michael@0: // of release. Use nsAutoRef::out() for return values, or an michael@0: // nsAutoRef modifiable lvalue for an out parameter. Use disown() when michael@0: // the reference must be stored in a POD type object, such as may be michael@0: // preferred for a namespace-scope object with static storage duration, michael@0: // for example. michael@0: RawRef disown() michael@0: { michael@0: RawRef temp = this->get(); michael@0: LocalSimpleRef empty; michael@0: SimpleRef::operator=(empty); michael@0: return temp; michael@0: } michael@0: michael@0: protected: michael@0: // steal and own are protected because they make no sense on nsReturnRef, michael@0: // but steal is implemented on this class for access to aOtherRef.disown() michael@0: // when aOtherRef is an nsReturnRef; michael@0: michael@0: // Transfer ownership from another smart reference. michael@0: void steal(ThisClass& aOtherRef) michael@0: { michael@0: own(aOtherRef.disown()); michael@0: } michael@0: // Assume ownership of a raw ref. michael@0: void own(RawRefOnly aRefToRelease) michael@0: { michael@0: SafeRelease(); michael@0: LocalSimpleRef ref(aRefToRelease); michael@0: SimpleRef::operator=(ref); michael@0: } michael@0: michael@0: // Release a resource if there is one. michael@0: void SafeRelease() michael@0: { michael@0: if (this->HaveResource()) michael@0: this->Release(this->get()); michael@0: } michael@0: }; michael@0: michael@0: #endif // !defined(nsAutoRef_h_)