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/. */
#ifndef mozilla_RefPtr_h
#define mozilla_RefPtr_h
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DbgMacro.h"
#include <type_traits>
/*****************************************************************************/
// template <class T> class RefPtrGetterAddRefs;
class nsQueryReferent;
class nsCOMPtr_helper;
class nsISupports;
namespace mozilla {
template <class T>
class MovingNotNull;
template <class T>
class NotNull;
template <class T>
class OwningNonNull;
template <class T>
class StaticLocalRefPtr;
template <class T>
class StaticRefPtr;
// Traditionally, RefPtr supports automatic refcounting of any pointer type
// with AddRef() and Release() methods that follow the traditional semantics.
//
// This traits class can be specialized to operate on other pointer types. For
// example, we specialize this trait for opaque FFI types that represent
// refcounted objects in Rust.
//
// Given the use of ConstRemovingRefPtrTraits below, U should not be a const-
// qualified type.
template <class U>
struct RefPtrTraits {
static void AddRef(U* aPtr) { aPtr->AddRef(); }
static void Release(U* aPtr) { aPtr->Release(); }
};
} // namespace mozilla
template <class T>
class MOZ_IS_REFPTR RefPtr {
private:
void assign_with_AddRef(T* aRawPtr) {
if (aRawPtr) {
ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr);
}
assign_assuming_AddRef(aRawPtr);
}
void assign_assuming_AddRef(T* aNewPtr) {
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
if (oldPtr) {
ConstRemovingRefPtrTraits<T>::Release(oldPtr);
}
}
private:
T* MOZ_OWNING_REF mRawPtr;
public:
typedef T element_type;
~RefPtr() {
if (mRawPtr) {
ConstRemovingRefPtrTraits<T>::Release(mRawPtr);
}
}
// Constructors
RefPtr()
: mRawPtr(nullptr)
// default constructor
{}
RefPtr(const RefPtr<T>& aSmartPtr)
: mRawPtr(aSmartPtr.mRawPtr)
// copy-constructor
{
if (mRawPtr) {
ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
}
}
RefPtr(RefPtr<T>&& aRefPtr) noexcept : mRawPtr(aRefPtr.mRawPtr) {
aRefPtr.mRawPtr = nullptr;
}
// construct from a raw pointer (of the right type)
MOZ_IMPLICIT RefPtr(T* aRawPtr) : mRawPtr(aRawPtr) {
if (mRawPtr) {
ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
}
}
MOZ_IMPLICIT RefPtr(decltype(nullptr)) : mRawPtr(nullptr) {}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
MOZ_IMPLICIT RefPtr(already_AddRefed<I>& aSmartPtr)
: mRawPtr(aSmartPtr.take())
// construct from |already_AddRefed|
{}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
MOZ_IMPLICIT RefPtr(already_AddRefed<I>&& aSmartPtr)
: mRawPtr(aSmartPtr.take())
// construct from |otherRefPtr.forget()|
{}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
MOZ_IMPLICIT RefPtr(const RefPtr<I>& aSmartPtr)
: mRawPtr(aSmartPtr.get())
// copy-construct from a smart pointer with a related pointer type
{
if (mRawPtr) {
ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
}
}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
MOZ_IMPLICIT RefPtr(RefPtr<I>&& aSmartPtr)
: mRawPtr(aSmartPtr.forget().take())
// construct from |Move(RefPtr<SomeSubclassOfT>)|.
{}
template <typename I,
typename = std::enable_if_t<!std::is_same_v<I, RefPtr<T>> &&
std::is_convertible_v<I, RefPtr<T>>>>
MOZ_IMPLICIT RefPtr(const mozilla::NotNull<I>& aSmartPtr)
: mRawPtr(RefPtr<T>(aSmartPtr.get()).forget().take())
// construct from |mozilla::NotNull|.
{}
template <typename I,
typename = std::enable_if_t<!std::is_same_v<I, RefPtr<T>> &&
std::is_convertible_v<I, RefPtr<T>>>>
MOZ_IMPLICIT RefPtr(mozilla::MovingNotNull<I>&& aSmartPtr)
: mRawPtr(RefPtr<T>(std::move(aSmartPtr).unwrapBasePtr()).forget().take())
// construct from |mozilla::MovingNotNull|.
{}
MOZ_IMPLICIT RefPtr(const nsQueryReferent& aHelper);
MOZ_IMPLICIT RefPtr(const nsCOMPtr_helper& aHelper);
// Defined in OwningNonNull.h
template <class U>
MOZ_IMPLICIT RefPtr(const mozilla::OwningNonNull<U>& aOther);
// Defined in StaticLocalPtr.h
template <class U>
MOZ_IMPLICIT RefPtr(const mozilla::StaticLocalRefPtr<U>& aOther);
// Defined in StaticPtr.h
template <class U>
MOZ_IMPLICIT RefPtr(const mozilla::StaticRefPtr<U>& aOther);
// Assignment operators
RefPtr<T>& operator=(decltype(nullptr)) {
assign_assuming_AddRef(nullptr);
return *this;
}
RefPtr<T>& operator=(const RefPtr<T>& aRhs)
// copy assignment operator
{
assign_with_AddRef(aRhs.mRawPtr);
return *this;
}
template <typename I>
RefPtr<T>& operator=(const RefPtr<I>& aRhs)
// assign from an RefPtr of a related pointer type
{
assign_with_AddRef(aRhs.get());
return *this;
}
RefPtr<T>& operator=(T* aRhs)
// assign from a raw pointer (of the right type)
{
assign_with_AddRef(aRhs);
return *this;
}
template <typename I>
RefPtr<T>& operator=(already_AddRefed<I>& aRhs)
// assign from |already_AddRefed|
{
assign_assuming_AddRef(aRhs.take());
return *this;
}
template <typename I>
RefPtr<T>& operator=(already_AddRefed<I>&& aRhs)
// assign from |otherRefPtr.forget()|
{
assign_assuming_AddRef(aRhs.take());
return *this;
}
RefPtr<T>& operator=(const nsQueryReferent& aQueryReferent);
RefPtr<T>& operator=(const nsCOMPtr_helper& aHelper);
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
RefPtr<T>& operator=(RefPtr<I>&& aRefPtr) noexcept {
assign_assuming_AddRef(aRefPtr.forget().take());
return *this;
}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I, RefPtr<T>>>>
RefPtr<T>& operator=(const mozilla::NotNull<I>& aSmartPtr)
// assign from |mozilla::NotNull|.
{
assign_assuming_AddRef(RefPtr<T>(aSmartPtr.get()).forget().take());
return *this;
}
template <typename I,
typename = std::enable_if_t<std::is_convertible_v<I, RefPtr<T>>>>
RefPtr<T>& operator=(mozilla::MovingNotNull<I>&& aSmartPtr)
// assign from |mozilla::MovingNotNull|.
{
assign_assuming_AddRef(
RefPtr<T>(std::move(aSmartPtr).unwrapBasePtr()).forget().take());
return *this;
}
// Defined in OwningNonNull.h
template <class U>
RefPtr<T>& operator=(const mozilla::OwningNonNull<U>& aOther);
// Defined in StaticLocalPtr.h
template <class U>
RefPtr<T>& operator=(const mozilla::StaticLocalRefPtr<U>& aOther);
// Defined in StaticPtr.h
template <class U>
RefPtr<T>& operator=(const mozilla::StaticRefPtr<U>& aOther);
// Other pointer operators
void swap(RefPtr<T>& aRhs)
// ...exchange ownership with |aRhs|; can save a pair of refcount operations
{
T* temp = aRhs.mRawPtr;
aRhs.mRawPtr = mRawPtr;
mRawPtr = temp;
}
void swap(T*& aRhs)
// ...exchange ownership with |aRhs|; can save a pair of refcount operations
{
T* temp = aRhs;
aRhs = mRawPtr;
mRawPtr = temp;
}
already_AddRefed<T> MOZ_MAY_CALL_AFTER_MUST_RETURN forget()
// return the value of mRawPtr and null out mRawPtr. Useful for
// already_AddRefed return values.
{
T* temp = nullptr;
swap(temp);
return already_AddRefed<T>(temp);
}
template <typename I>
void forget(I** aRhs)
// Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
// Useful to avoid unnecessary AddRef/Release pairs with "out"
// parameters where aRhs bay be a T** or an I** where I is a base class
// of T.
{
MOZ_ASSERT(aRhs, "Null pointer passed to forget!");
*aRhs = mRawPtr;
mRawPtr = nullptr;
}
void forget(nsISupports** aRhs) {
MOZ_ASSERT(aRhs, "Null pointer passed to forget!");
*aRhs = ToSupports(mRawPtr);
mRawPtr = nullptr;
}
T* get() const
/*
Prefer the implicit conversion provided automatically by |operator T*()
const|. Use |get()| to resolve ambiguity or to get a castable pointer.
*/
{
return const_cast<T*>(mRawPtr);
}
operator T*() const&
/*
...makes an |RefPtr| act like its underlying raw pointer type whenever it
is used in a context where a raw pointer is expected. It is this operator
that makes an |RefPtr| substitutable for a raw pointer.
Prefer the implicit use of this operator to calling |get()|, except where
necessary to resolve ambiguity.
*/
{
return get();
}
// Don't allow implicit conversion of temporary RefPtr to raw pointer,
// because the refcount might be one and the pointer will immediately become
// invalid.
operator T*() const&& = delete;
// These are needed to avoid the deleted operator above. XXX Why is operator!
// needed separately? Shouldn't the compiler prefer using the non-deleted
// operator bool instead of the deleted operator T*?
explicit operator bool() const { return !!mRawPtr; }
bool operator!() const { return !mRawPtr; }
T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
MOZ_ASSERT(mRawPtr != nullptr,
"You can't dereference a NULL RefPtr with operator->().");
return get();
}
template <typename R, typename... Args>
class Proxy {
typedef R (T::*member_function)(Args...);
T* mRawPtr;
member_function mFunction;
public:
Proxy(T* aRawPtr, member_function aFunction)
: mRawPtr(aRawPtr), mFunction(aFunction) {}
template <typename... ActualArgs>
R operator()(ActualArgs&&... aArgs) {
return ((*