Source code

Revision control

Copy as Markdown

Other Tools

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