gecko/xpcom/base/StaticPtr.h

237 lines
6.9 KiB
C++

/* -*- 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_StaticPtr_h
#define mozilla_StaticPtr_h
#include "mozilla/Assertions.h"
#include "mozilla/NullPtr.h"
namespace mozilla {
/**
* StaticAutoPtr and StaticRefPtr are like nsAutoPtr and nsRefPtr, except they
* are suitable for use as global variables.
*
* In particular, a global instance of Static{Auto,Ref}Ptr doesn't cause the
* compiler to emit a static initializer (in release builds, anyway).
*
* In order to accomplish this, Static{Auto,Ref}Ptr must have a trivial
* constructor and destructor. As a consequence, it cannot initialize its raw
* pointer to 0 on construction, and it cannot delete/release its raw pointer
* upon destruction.
*
* Since the compiler guarantees that all global variables are initialized to
* 0, these trivial constructors are safe, so long as you use
* Static{Auto,Ref}Ptr as a global variable. If you use Static{Auto,Ref}Ptr as
* a stack variable or as a class instance variable, you will get what you
* deserve.
*
* Static{Auto,Ref}Ptr have a limited interface as compared to ns{Auto,Ref}Ptr;
* this is intentional, since their range of acceptable uses is smaller.
*/
template<class T>
class StaticAutoPtr
{
public:
// In debug builds, check that mRawPtr is initialized for us as we expect
// by the compiler. In non-debug builds, don't declare a constructor
// so that the compiler can see that the constructor is trivial.
#ifdef DEBUG
StaticAutoPtr()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticAutoPtr<T>& operator=(T* aRhs)
{
Assign(aRhs);
return *this;
}
T* get() const { return mRawPtr; }
operator T*() const { return get(); }
T* operator->() const
{
MOZ_ASSERT(mRawPtr);
return get();
}
T& operator*() const { return *get(); }
private:
// Disallow copy constructor, but only in debug mode. We only define
// a default constructor in debug mode (see above); if we declared
// this constructor always, the compiler wouldn't generate a trivial
// default constructor for us in non-debug mode.
#ifdef DEBUG
StaticAutoPtr(StaticAutoPtr<T>& aOther);
#endif
void Assign(T* aNewPtr)
{
MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr);
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
delete oldPtr;
}
T* mRawPtr;
};
template<class T>
class StaticRefPtr
{
public:
// In debug builds, check that mRawPtr is initialized for us as we expect
// by the compiler. In non-debug builds, don't declare a constructor
// so that the compiler can see that the constructor is trivial.
#ifdef DEBUG
StaticRefPtr()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticRefPtr<T>& operator=(T* aRhs)
{
AssignWithAddref(aRhs);
return *this;
}
StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs)
{
return (this = aRhs.mRawPtr);
}
T* get() const { return mRawPtr; }
operator T*() const { return get(); }
T* operator->() const
{
MOZ_ASSERT(mRawPtr);
return get();
}
T& operator*() const { return *get(); }
private:
void AssignWithAddref(T* aNewPtr)
{
if (aNewPtr) {
aNewPtr->AddRef();
}
AssignAssumingAddRef(aNewPtr);
}
void AssignAssumingAddRef(T* aNewPtr)
{
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
if (oldPtr) {
oldPtr->Release();
}
}
T* mRawPtr;
};
namespace StaticPtr_internal {
class Zero;
} // namespace StaticPtr_internal
#define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \
template<__VA_ARGS__> \
inline bool \
operator==(type1 lhs, type2 rhs) \
{ \
return eq_fn; \
} \
\
template<__VA_ARGS__> \
inline bool \
operator==(type2 lhs, type1 rhs) \
{ \
return rhs == lhs; \
} \
\
template<__VA_ARGS__> \
inline bool \
operator!=(type1 lhs, type2 rhs) \
{ \
return !(lhs == rhs); \
} \
\
template<__VA_ARGS__> \
inline bool \
operator!=(type2 lhs, type1 rhs) \
{ \
return !(lhs == rhs); \
}
// StaticAutoPtr (in)equality operators
template<class T, class U>
inline bool
operator==(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticAutoPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
// StaticRefPtr (in)equality operators
template<class T, class U>
inline bool
operator==(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticRefPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
#undef REFLEXIVE_EQUALITY_OPERATORS
} // namespace mozilla
#endif