aboutsummaryrefslogtreecommitdiff
path: root/include/EASTL/intrusive_ptr.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/EASTL/intrusive_ptr.h')
-rw-r--r--include/EASTL/intrusive_ptr.h426
1 files changed, 426 insertions, 0 deletions
diff --git a/include/EASTL/intrusive_ptr.h b/include/EASTL/intrusive_ptr.h
new file mode 100644
index 0000000..af4e686
--- /dev/null
+++ b/include/EASTL/intrusive_ptr.h
@@ -0,0 +1,426 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EASTL_INTRUSIVE_PTR_H
+#define EASTL_INTRUSIVE_PTR_H
+
+
+#include <EASTL/internal/config.h>
+#include <stddef.h>
+
+#if defined(EA_PRAGMA_ONCE_SUPPORTED)
+ #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
+#endif
+
+
+
+namespace eastl
+{
+ // We provide default implementations of AddRef and Release in the eastl namespace.
+ // The user can override these on a per-class basis by defining their own specialized
+ // intrusive_ptr_add_ref and intrusive_ptr_release functions. User-defined specializations
+ // do not need to exist in the eastl namespace, but should preferably be in the namespace
+ // of the templated class T.
+ template <typename T>
+ void intrusive_ptr_add_ref(T* p)
+ {
+ p->AddRef();
+ }
+
+ template <typename T>
+ void intrusive_ptr_release(T* p)
+ {
+ p->Release();
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////
+ /// intrusive_ptr
+ ///
+ /// This is a class that acts like the C++ auto_ptr class except that instead
+ /// of deleting its member data when it goes out of scope, it releases its
+ /// member data when it goes out of scope. This class thus requires that the
+ /// templated data type have an AddRef and Release function (or whatever is
+ /// configured to be the two refcount functions).
+ ///
+ /// This class is useful for automatically releasing an object when this
+ /// class goes out of scope. See below for some usage.
+ /// You should be careful about putting instances of this class as members of
+ /// another class. If you do so, then the intrusive_ptr destructor will only
+ /// be called if the object that owns it is destructed. This creates a potential
+ /// chicken-and-egg situation. What if the intrusive_ptr member contains a
+ /// pointer to an object that has a reference on the object that owns the
+ /// intrusive_ptr member? The answer is that the neither object can ever be
+ /// destructed. The solution is to:
+ /// 1) Be very careful about what objects you put into member intrusive_ptr objects.
+ /// 2) Clear out your intrusive_ptr members in your shutdown function.
+ /// 3) Simply don't use intrusive_ptr objects as class members.
+ ///
+ /// Example usage:
+ /// intrusive_ptr<IWidget> pWidget = new Widget;
+ /// pWidget = new Widget;
+ /// pWidget->Reset();
+ ///
+ template <typename T>
+ class intrusive_ptr
+ {
+ protected:
+ // Friend declarations.
+ template <typename U> friend class intrusive_ptr;
+ typedef intrusive_ptr<T> this_type;
+
+ T* mpObject;
+
+ public:
+ /// element_type
+ /// This typedef is present for consistency with the C++ standard library
+ /// auto_ptr template. It allows users to refer to the templated type via
+ /// a typedef. This is sometimes useful to be able to do.
+ ///
+ /// Example usage:
+ /// intrusive_ptr<IWidget> ip;
+ /// void DoSomething(intrusive_ptr<IWidget>::element_type someType);
+ ///
+ typedef T element_type;
+
+ /// intrusive_ptr
+ /// Default constructor. The member object is set to NULL.
+ intrusive_ptr()
+ : mpObject(NULL)
+ {
+ // Empty
+ }
+
+ /// intrusive_ptr
+ /// Provides a constructor which takes ownership of a pointer.
+ /// The incoming pointer is AddRefd.
+ ///
+ /// Example usage:
+ /// intrusive_ptr<Widget> pWidget(new Widget);
+ intrusive_ptr(T* p, bool bAddRef = true)
+ : mpObject(p)
+ {
+ if(mpObject && bAddRef)
+ intrusive_ptr_add_ref(mpObject); // Intentionally do not prefix the call with eastl:: but instead allow namespace lookup to resolve the namespace.
+ }
+
+ /// intrusive_ptr
+ /// Construction from self type.
+ intrusive_ptr(const intrusive_ptr& ip)
+ : mpObject(ip.mpObject)
+ {
+ if(mpObject)
+ intrusive_ptr_add_ref(mpObject);
+ }
+
+
+ /// intrusive_ptr
+ /// move constructor
+ intrusive_ptr(intrusive_ptr&& ip)
+ : mpObject(nullptr)
+ {
+ swap(ip);
+ }
+
+ /// intrusive_ptr
+ /// Provides a constructor which copies a pointer from another intrusive_ptr.
+ /// The incoming pointer is AddRefd. The source intrusive_ptr object maintains
+ /// its AddRef on the pointer.
+ ///
+ /// Example usage:
+ /// intrusive_ptr<Widget> pWidget1;
+ /// intrusive_ptr<Widget> pWidget2(pWidget1);
+ template <typename U>
+ intrusive_ptr(const intrusive_ptr<U>& ip)
+ : mpObject(ip.mpObject)
+ {
+ if(mpObject)
+ intrusive_ptr_add_ref(mpObject);
+ }
+
+ /// intrusive_ptr
+ /// Releases the owned pointer.
+ ~intrusive_ptr()
+ {
+ if(mpObject)
+ intrusive_ptr_release(mpObject);
+ }
+
+
+ /// operator=
+ /// Assignment to self type.
+ intrusive_ptr& operator=(const intrusive_ptr& ip)
+ {
+ return operator=(ip.mpObject);
+ }
+
+
+ /// operator=
+ /// Move assignment operator
+ intrusive_ptr& operator=(intrusive_ptr&& ip)
+ {
+ swap(ip);
+ return *this;
+ }
+
+
+ /// operator =
+ /// Assigns an intrusive_ptr object to this intrusive_ptr object.
+ /// The incoming pointer is AddRefd. The source intrusive_ptr object
+ /// maintains its AddRef on the pointer. If there is an existing member
+ /// pointer, it is Released before the incoming pointer is assigned.
+ /// If the incoming pointer is equal to the existing pointer, no
+ /// action is taken. The incoming pointer is AddRefd before any
+ /// member pointer is Released.
+ template <typename U>
+ intrusive_ptr& operator=(const intrusive_ptr<U>& ip)
+ {
+ return operator=(ip.mpObject);
+ }
+
+ /// operator=
+ /// Assigns an intrusive_ptr object to this intrusive_ptr object.
+ /// The incoming pointer is AddRefd. If there is an existing member
+ /// pointer, it is Released before the incoming pointer is assigned.
+ /// If the incoming pointer is equal to the existing pointer, no
+ /// action is taken. The incoming pointer is AddRefd before any
+ /// member pointer is Released.
+ intrusive_ptr& operator=(T* pObject)
+ {
+ if(pObject != mpObject)
+ {
+ T* const pTemp = mpObject; // Create temporary to prevent possible problems with re-entrancy.
+ if(pObject)
+ intrusive_ptr_add_ref(pObject);
+ mpObject = pObject;
+ if(pTemp)
+ intrusive_ptr_release(pTemp);
+ }
+ return *this;
+ }
+
+ /// operator *
+ /// Returns a reference to the contained object.
+ T& operator *() const
+ {
+ return *mpObject;
+ }
+
+ /// operator *
+ /// Returns a pointer to the contained object, allowing the
+ /// user to use this container as if it were contained pointer itself.
+ T* operator ->() const
+ {
+ return mpObject;
+ }
+
+ /// get()
+ /// Returns a pointer to the contained object.
+ T* get() const
+ {
+ return mpObject;
+ }
+
+ /// reset
+ /// Releases the owned object and clears our reference to it.
+ void reset()
+ {
+ T* const pTemp = mpObject;
+ mpObject = NULL;
+ if(pTemp)
+ intrusive_ptr_release(pTemp);
+ }
+
+ /// swap
+ /// Exchanges the owned pointer beween two intrusive_ptr objects.
+ void swap(this_type& ip)
+ {
+ T* const pTemp = mpObject;
+ mpObject = ip.mpObject;
+ ip.mpObject = pTemp;
+ }
+
+ /// attach
+ /// Sets an intrusive_ptr pointer without calling AddRef() on
+ /// the pointed object. The intrusive_ptr thus eventually only does a
+ /// Release() on the object. This is useful for assuming a reference
+ /// that someone else has handed you and making sure it is always
+ /// released, even if you return in the middle of a function or an
+ /// exception is thrown.
+ ///
+ void attach(T* pObject)
+ {
+ T* const pTemp = mpObject;
+ mpObject = pObject;
+ if(pTemp)
+ intrusive_ptr_release(pTemp);
+ }
+
+ /// detach
+ /// Surrenders the reference held by an intrusive_ptr pointer --
+ /// it returns the current reference and nulls the pointer. If the returned
+ /// pointer is non-null it must be released. This is useful in functions
+ /// that must return a reference while possibly being aborted by a return
+ /// or thrown exception:
+ ///
+ /// bool GetFoo(T** pp){
+ /// intrusive_ptr<T> p(PrivateGetFoo());
+ /// if(p->Method())
+ /// return false;
+ /// *pp = p.detach();
+ /// return true;
+ /// }
+ T* detach()
+ {
+ T* const pTemp = mpObject;
+ mpObject = NULL;
+ return pTemp;
+ }
+
+ /// Implicit operator bool
+ /// Allows for using a intrusive_ptr as a boolean.
+ /// Example usage:
+ /// intrusive_ptr<Widget> ptr = new Widget;
+ /// if(ptr)
+ /// ++*ptr;
+ ///
+ /// Note that below we do not use operator bool(). The reason for this
+ /// is that booleans automatically convert up to short, int, float, etc.
+ /// The result is that this: if(intrusivePtr == 1) would yield true (bad).
+ typedef T* (this_type::*bool_)() const;
+ operator bool_() const
+ {
+ if(mpObject)
+ return &this_type::get;
+ return NULL;
+ }
+
+ /// operator!
+ /// This returns the opposite of operator bool; it returns true if
+ /// the owned pointer is null. Some compilers require this and some don't.
+ /// intrusive_ptr<Widget> ptr = new Widget;
+ /// if(!ptr)
+ /// assert(false);
+ bool operator!() const
+ {
+ return (mpObject == NULL);
+ }
+
+ }; // class intrusive_ptr
+
+
+ /// get_pointer
+ /// returns intrusive_ptr::get() via the input intrusive_ptr.
+ template <typename T>
+ inline T* get_pointer(const intrusive_ptr<T>& intrusivePtr)
+ {
+ return intrusivePtr.get();
+ }
+
+ /// swap
+ /// Exchanges the owned pointer beween two intrusive_ptr objects.
+ /// This non-member version is useful for compatibility of intrusive_ptr
+ /// objects with the C++ Standard Library and other libraries.
+ template <typename T>
+ inline void swap(intrusive_ptr<T>& intrusivePtr1, intrusive_ptr<T>& intrusivePtr2)
+ {
+ intrusivePtr1.swap(intrusivePtr2);
+ }
+
+
+ template <typename T, typename U>
+ bool operator==(intrusive_ptr<T> const& iPtr1, intrusive_ptr<U> const& iPtr2)
+ {
+ return (iPtr1.get() == iPtr2.get());
+ }
+
+ template <typename T, typename U>
+ bool operator!=(intrusive_ptr<T> const& iPtr1, intrusive_ptr<U> const& iPtr2)
+ {
+ return (iPtr1.get() != iPtr2.get());
+ }
+
+ template <typename T>
+ bool operator==(intrusive_ptr<T> const& iPtr1, T* p)
+ {
+ return (iPtr1.get() == p);
+ }
+
+ template <typename T>
+ bool operator!=(intrusive_ptr<T> const& iPtr1, T* p)
+ {
+ return (iPtr1.get() != p);
+ }
+
+ template <typename T>
+ bool operator==(T* p, intrusive_ptr<T> const& iPtr2)
+ {
+ return (p == iPtr2.get());
+ }
+
+ template <typename T>
+ bool operator!=(T* p, intrusive_ptr<T> const& iPtr2)
+ {
+ return (p != iPtr2.get());
+ }
+
+ template <typename T, typename U>
+ bool operator<(intrusive_ptr<T> const& iPtr1, intrusive_ptr<U> const& iPtr2)
+ {
+ return ((uintptr_t)iPtr1.get() < (uintptr_t)iPtr2.get());
+ }
+
+
+ /// static_pointer_cast
+ /// Returns an intrusive_ptr<T> static-casted from a intrusive_ptr<U>.
+ template <class T, class U>
+ intrusive_ptr<T> static_pointer_cast(const intrusive_ptr<U>& intrusivePtr)
+ {
+ return static_cast<T*>(intrusivePtr.get());
+ }
+
+
+ #if EASTL_RTTI_ENABLED
+
+ /// dynamic_pointer_cast
+ /// Returns an intrusive_ptr<T> dynamic-casted from a intrusive_ptr<U>.
+ template <class T, class U>
+ intrusive_ptr<T> dynamic_pointer_cast(const intrusive_ptr<U>& intrusivePtr)
+ {
+ return dynamic_cast<T*>(intrusivePtr.get());
+ }
+
+ #endif
+
+
+} // namespace eastl
+
+
+#endif // Header include guard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+