michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 nsTWeakRef_h__
michael@0: #define nsTWeakRef_h__
michael@0:
michael@0: #ifndef nsDebug_h___
michael@0: #include "nsDebug.h"
michael@0: #endif
michael@0:
michael@0: /**
michael@0: * A weak reference class for use with generic C++ objects. NOT THREADSAFE!
michael@0: *
michael@0: * Example usage:
michael@0: *
michael@0: * class A {
michael@0: * public:
michael@0: * A() : mWeakSelf(this) {
michael@0: * }
michael@0: * ~A() {
michael@0: * mWeakSelf.forget();
michael@0: * }
michael@0: * void Bar() { printf("Bar!\n"); }
michael@0: * const nsTWeakRef &AsWeakRef() const { return mWeakSelf; }
michael@0: * private:
michael@0: * nsTWeakRef mWeakSelf;
michael@0: * };
michael@0: *
michael@0: * class B {
michael@0: * public:
michael@0: * void SetA(const nsTWeakRef &a) {
michael@0: * mA = a;
michael@0: * }
michael@0: * void Foo() {
michael@0: * if (mA)
michael@0: * mA->Bar();
michael@0: * }
michael@0: * private:
michael@0: * nsTWeakRef mA;
michael@0: * };
michael@0: *
michael@0: * void Test() {
michael@0: * B b;
michael@0: * {
michael@0: * A a;
michael@0: * b.SetA(a.AsWeakRef());
michael@0: * b.Foo(); // prints "Bar!"
michael@0: * }
michael@0: * b.Foo(); // prints nothing because |a| has already been destroyed
michael@0: * }
michael@0: *
michael@0: * One can imagine much more complex examples, especially when asynchronous
michael@0: * event processing is involved.
michael@0: *
michael@0: * Keep in mind that you should only ever need a class like this when you have
michael@0: * multiple instances of B, such that it is not possible for A and B to simply
michael@0: * have pointers to one another.
michael@0: */
michael@0: template
michael@0: class nsTWeakRef {
michael@0: public:
michael@0: ~nsTWeakRef() {
michael@0: if (mRef)
michael@0: mRef->Release();
michael@0: }
michael@0:
michael@0: /**
michael@0: * Construct from an object pointer (may be null).
michael@0: */
michael@0: explicit
michael@0: nsTWeakRef(Type *obj = nullptr) {
michael@0: if (obj) {
michael@0: mRef = new Inner(obj);
michael@0: } else {
michael@0: mRef = nullptr;
michael@0: }
michael@0: }
michael@0:
michael@0: /**
michael@0: * Construct from another weak reference object.
michael@0: */
michael@0: explicit
michael@0: nsTWeakRef(const nsTWeakRef &other) : mRef(other.mRef) {
michael@0: if (mRef)
michael@0: mRef->AddRef();
michael@0: }
michael@0:
michael@0: /**
michael@0: * Assign from an object pointer.
michael@0: */
michael@0: nsTWeakRef &operator=(Type *obj) {
michael@0: if (mRef)
michael@0: mRef->Release();
michael@0: if (obj) {
michael@0: mRef = new Inner(obj);
michael@0: } else {
michael@0: mRef = nullptr;
michael@0: }
michael@0: return *this;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Assign from another weak reference object.
michael@0: */
michael@0: nsTWeakRef &operator=(const nsTWeakRef &other) {
michael@0: if (mRef)
michael@0: mRef->Release();
michael@0: mRef = other.mRef;
michael@0: if (mRef)
michael@0: mRef->AddRef();
michael@0: return *this;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Get the referenced object. This method may return null if the reference
michael@0: * has been cleared or if an out-of-memory error occurred at assignment.
michael@0: */
michael@0: Type *get() const {
michael@0: return mRef ? mRef->mObj : nullptr;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Called to "null out" the weak reference. Typically, the object referenced
michael@0: * by this weak reference calls this method when it is being destroyed.
michael@0: * @returns The former referenced object.
michael@0: */
michael@0: Type *forget() {
michael@0: Type *obj;
michael@0: if (mRef) {
michael@0: obj = mRef->mObj;
michael@0: mRef->mObj = nullptr;
michael@0: mRef->Release();
michael@0: mRef = nullptr;
michael@0: } else {
michael@0: obj = nullptr;
michael@0: }
michael@0: return obj;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Allow |*this| to be treated as a |Type*| for convenience.
michael@0: */
michael@0: operator Type *() const {
michael@0: return get();
michael@0: }
michael@0:
michael@0: /**
michael@0: * Allow |*this| to be treated as a |Type*| for convenience. Use with
michael@0: * caution since this method will crash if the referenced object is null.
michael@0: */
michael@0: Type *operator->() const {
michael@0: NS_ASSERTION(mRef && mRef->mObj,
michael@0: "You can't dereference a null weak reference with operator->().");
michael@0: return get();
michael@0: }
michael@0:
michael@0: private:
michael@0:
michael@0: struct Inner {
michael@0: int mCnt;
michael@0: Type *mObj;
michael@0:
michael@0: Inner(Type *obj) : mCnt(1), mObj(obj) {}
michael@0: void AddRef() { ++mCnt; }
michael@0: void Release() { if (--mCnt == 0) delete this; }
michael@0: };
michael@0:
michael@0: Inner *mRef;
michael@0: };
michael@0:
michael@0: #endif // nsTWeakRef_h__