Back to index

enigmail  1.4.3
RefPtr.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at:
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  *   The Mozilla Foundation
00021  * Portions created by the Initial Developer are Copyright (C) 2011
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Chris Jones <jones.chris.g@gmail.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* Helpers for defining and using refcounted objects. */
00042 
00043 #ifndef mozilla_RefPtr_h_
00044 #define mozilla_RefPtr_h_
00045 
00046 #include "mozilla/Assertions.h"
00047 #include "mozilla/Attributes.h"
00048 
00049 namespace mozilla {
00050 
00051 template<typename T> class RefCounted;
00052 template<typename T> class RefPtr;
00053 template<typename T> class TemporaryRef;
00054 template<typename T> class OutParamRef;
00055 template<typename T> OutParamRef<T> byRef(RefPtr<T>&);
00056 
00078 template<typename T>
00079 class RefCounted
00080 {
00081     friend class RefPtr<T>;
00082 
00083 public:
00084     RefCounted() : refCnt(0) { }
00085     ~RefCounted() { MOZ_ASSERT(refCnt == -0xdead); }
00086 
00087     // Compatibility with nsRefPtr.
00088     void AddRef() {
00089         MOZ_ASSERT(refCnt >= 0);
00090         ++refCnt;
00091     }
00092 
00093     void Release() {
00094         MOZ_ASSERT(refCnt > 0);
00095         if (0 == --refCnt) {
00096 #ifdef DEBUG
00097             refCnt = -0xdead;
00098 #endif
00099             delete static_cast<T*>(this);
00100         }
00101     }
00102 
00103     // Compatibility with wtf::RefPtr.
00104     void ref() { AddRef(); }
00105     void deref() { Release(); }
00106     int refCount() const { return refCnt; }
00107     bool hasOneRef() const {
00108         MOZ_ASSERT(refCnt > 0);
00109         return refCnt == 1;
00110     }
00111 
00112 private:
00113     int refCnt;
00114 };
00115 
00126 template<typename T>
00127 class RefPtr
00128 {
00129     // To allow them to use unref()
00130     friend class TemporaryRef<T>;
00131     friend class OutParamRef<T>;
00132 
00133     struct dontRef {};
00134 
00135 public:
00136     RefPtr() : ptr(0) { }
00137     RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {}
00138     RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {}
00139     RefPtr(T* t) : ptr(ref(t)) {}
00140 
00141     template<typename U>
00142     RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {}
00143 
00144     ~RefPtr() { unref(ptr); }
00145 
00146     RefPtr& operator=(const RefPtr& o) {
00147         assign(ref(o.ptr));
00148         return *this;
00149     }
00150     RefPtr& operator=(const TemporaryRef<T>& o) {
00151         assign(o.drop());
00152         return *this;
00153     }
00154     RefPtr& operator=(T* t) {
00155         assign(ref(t));
00156         return *this;
00157     }
00158 
00159     template<typename U>
00160     RefPtr& operator=(const RefPtr<U>& o) {
00161         assign(ref(o.get()));
00162         return *this;
00163     }
00164 
00165     TemporaryRef<T> forget() {
00166         T* tmp = ptr;
00167         ptr = 0;
00168         return TemporaryRef<T>(tmp, dontRef());
00169     }
00170 
00171     T* get() const { return ptr; }
00172     operator T*() const { return ptr; }
00173     T* operator->() const { return ptr; }
00174     T& operator*() const { return *ptr; }
00175     template<typename U>
00176     operator TemporaryRef<U>() { return TemporaryRef<U>(ptr); }
00177 
00178 private:
00179     void assign(T* t) {
00180         unref(ptr);
00181         ptr = t;
00182     }
00183 
00184     T* ptr;
00185 
00186     static MOZ_ALWAYS_INLINE T* ref(T* t) {
00187         if (t) {
00188             t->AddRef();
00189         }
00190         return t;
00191     }
00192 
00193     static MOZ_ALWAYS_INLINE void unref(T* t) {
00194         if (t) {
00195             t->Release();
00196         }
00197     }
00198 };
00199 
00206 template<typename T>
00207 class TemporaryRef
00208 {
00209     // To allow it to construct TemporaryRef from a bare T*
00210     friend class RefPtr<T>;
00211 
00212     typedef typename RefPtr<T>::dontRef dontRef;
00213 
00214 public:
00215     TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {}
00216     TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {}
00217 
00218     template<typename U>
00219     TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {}
00220 
00221     ~TemporaryRef() { RefPtr<T>::unref(ptr); }
00222 
00223     T* drop() const {
00224         T* tmp = ptr;
00225         ptr = 0;
00226         return tmp;
00227     }
00228 
00229 private:
00230     TemporaryRef(T* t, const dontRef&) : ptr(t) {}
00231 
00232     mutable T* ptr;
00233 
00234     TemporaryRef();
00235     TemporaryRef& operator=(const TemporaryRef&);
00236 };
00237 
00252 template<typename T>
00253 class OutParamRef
00254 {
00255     friend OutParamRef byRef<T>(RefPtr<T>&);
00256 
00257 public:
00258     ~OutParamRef() {
00259         RefPtr<T>::unref(refPtr.ptr);
00260         refPtr.ptr = tmp;
00261     }
00262 
00263     operator T**() { return &tmp; }
00264 
00265 private:
00266     OutParamRef(RefPtr<T>& p) : refPtr(p), tmp(p.get()) {}
00267 
00268     RefPtr<T>& refPtr;
00269     T* tmp;
00270 
00271     OutParamRef() MOZ_DELETE;
00272     OutParamRef& operator=(const OutParamRef&) MOZ_DELETE;
00273 };
00274 
00278 template<typename T>
00279 OutParamRef<T>
00280 byRef(RefPtr<T>& ptr)
00281 {
00282     return OutParamRef<T>(ptr);
00283 }
00284 
00285 } // namespace mozilla
00286 
00287 #endif // mozilla_RefPtr_h_
00288 
00289 
00290 #if 0
00291 
00292 // Command line that builds these tests
00293 //
00294 //   cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test
00295 
00296 using namespace mozilla;
00297 
00298 struct Foo : public RefCounted<Foo>
00299 {
00300     Foo() : dead(false) { }
00301     ~Foo() {
00302         MOZ_ASSERT(!dead);
00303         dead = true;
00304         numDestroyed++;
00305     }
00306 
00307     bool dead;
00308     static int numDestroyed;
00309 };
00310 int Foo::numDestroyed;
00311 
00312 struct Bar : public Foo { };
00313 
00314 TemporaryRef<Foo>
00315 NewFoo()
00316 {
00317     return RefPtr<Foo>(new Foo());
00318 }
00319 
00320 TemporaryRef<Foo>
00321 NewBar()
00322 {
00323     return new Bar();
00324 }
00325 
00326 void
00327 GetNewFoo(Foo** f)
00328 {
00329     *f = new Bar();
00330     // Kids, don't try this at home
00331     (*f)->AddRef();
00332 }
00333 
00334 void
00335 GetPassedFoo(Foo** f)
00336 {
00337     // Kids, don't try this at home
00338     (*f)->AddRef();
00339 }
00340 
00341 void
00342 GetNewFoo(RefPtr<Foo>* f)
00343 {
00344     *f = new Bar();
00345 }
00346 
00347 void
00348 GetPassedFoo(RefPtr<Foo>* f)
00349 {}
00350 
00351 TemporaryRef<Foo>
00352 GetNullFoo()
00353 {
00354     return 0;
00355 }
00356 
00357 int
00358 main(int argc, char** argv)
00359 {
00360     // This should blow up
00361 //    Foo* f = new Foo(); delete f;
00362 
00363     MOZ_ASSERT(0 == Foo::numDestroyed);
00364     {
00365         RefPtr<Foo> f = new Foo();
00366         MOZ_ASSERT(f->refCount() == 1);
00367     }
00368     MOZ_ASSERT(1 == Foo::numDestroyed);
00369 
00370     {
00371         RefPtr<Foo> f1 = NewFoo();
00372         RefPtr<Foo> f2(NewFoo());
00373         MOZ_ASSERT(1 == Foo::numDestroyed);
00374     }
00375     MOZ_ASSERT(3 == Foo::numDestroyed);
00376 
00377     {
00378         RefPtr<Foo> b = NewBar();
00379         MOZ_ASSERT(3 == Foo::numDestroyed);
00380     }
00381     MOZ_ASSERT(4 == Foo::numDestroyed);
00382 
00383     {
00384         RefPtr<Foo> f1;
00385         {
00386             f1 = new Foo();
00387             RefPtr<Foo> f2(f1);
00388             RefPtr<Foo> f3 = f2;
00389             MOZ_ASSERT(4 == Foo::numDestroyed);
00390         }
00391         MOZ_ASSERT(4 == Foo::numDestroyed);
00392     }
00393     MOZ_ASSERT(5 == Foo::numDestroyed);
00394 
00395     {
00396         RefPtr<Foo> f = new Foo();
00397         f.forget();
00398         MOZ_ASSERT(6 == Foo::numDestroyed);
00399     }
00400 
00401     {
00402         RefPtr<Foo> f = new Foo();
00403         GetNewFoo(byRef(f));
00404         MOZ_ASSERT(7 == Foo::numDestroyed);
00405     }
00406     MOZ_ASSERT(8 == Foo::numDestroyed);
00407 
00408     {
00409         RefPtr<Foo> f = new Foo();
00410         GetPassedFoo(byRef(f));
00411         MOZ_ASSERT(8 == Foo::numDestroyed);
00412     }
00413     MOZ_ASSERT(9 == Foo::numDestroyed);
00414 
00415     {
00416         RefPtr<Foo> f = new Foo();
00417         GetNewFoo(&f);
00418         MOZ_ASSERT(10 == Foo::numDestroyed);
00419     }
00420     MOZ_ASSERT(11 == Foo::numDestroyed);
00421 
00422     {
00423         RefPtr<Foo> f = new Foo();
00424         GetPassedFoo(&f);
00425         MOZ_ASSERT(11 == Foo::numDestroyed);
00426     }
00427     MOZ_ASSERT(12 == Foo::numDestroyed);
00428 
00429     {
00430         RefPtr<Foo> f1 = new Bar();
00431     }
00432     MOZ_ASSERT(13 == Foo::numDestroyed);
00433 
00434     {
00435         RefPtr<Foo> f = GetNullFoo();
00436         MOZ_ASSERT(13 == Foo::numDestroyed);
00437     }
00438     MOZ_ASSERT(13 == Foo::numDestroyed);
00439 
00440     return 0;
00441 }
00442 
00443 #endif