//////////////////////////////////////////////////////////////// // $Id: IHandle.hxx,v 1.33.14.1 2013/01/11 16:57:06 mcgrew Exp $ // #ifndef THandle_hxx_seen #define THandle_hxx_seen #include #include #include #include "EoaCore.hxx" #include "ICOMETLog.hxx" namespace COMET { OA_EXCEPTION(EHandle,EoaCore); OA_EXCEPTION(EHandleBadReference,EHandle); class IHandleBase; class IHandleBaseDeletable; class IHandleBaseUndeletable; class IVHandle; template class IHandle; template T* GetPointer(const IHandle& handle); template bool operator < (const IHandle& a, const IHandle&b); /// An abstract base class to implement the reference counted internal /// object. The IHandleBase objects contain the actual pointer that is /// being reference counted. When the IHandleBase object is deleted, the /// pointer is also deleted. This object maintains the reference count. class IHandleBase : public TObject { public: IHandleBase(); virtual ~IHandleBase(); int GetReferenceCount() const {return fCount;} void DecrementReferenceCount() {--fCount;} void IncrementReferenceCount() {++fCount;} virtual TObject* GetObject() const = 0; void Release() {SetBit(kPointerReleased);} bool IsOwner() {return !TestBit(kPointerReleased);} private: /// Define the status bits used by the IHandle object. These can't /// collide with any status bits defined in TObject (the parent class /// for IHandleBase), and none of the IHandleBase children can define /// a status bit that collides with these definitions. Bits 14 to 23 /// are available for use. enum EStatusBits { kPointerReleased = BIT(20) }; int fCount; ClassDef(IHandleBase,2); }; /// A concrete version of the IHandleBase class for pointers that should /// be deleted when the last reference goes away. The reference count is /// maintained in IHandleBase. class IHandleBaseDeletable : public IHandleBase { public: IHandleBaseDeletable(); IHandleBaseDeletable(TObject* object); virtual ~IHandleBaseDeletable(); TObject* GetObject() const {return fObject;} private: /// The actual pointer that will be reference counted. TObject* fObject; ClassDef(IHandleBaseDeletable,2); }; /// A concrete version of the IHandleBase class for pointers that should /// be deleted when the last reference goes away. The reference count is /// maintained in IHandleBase. class IHandleBaseUndeletable : public IHandleBase { public: IHandleBaseUndeletable(); IHandleBaseUndeletable(TObject* object); virtual ~IHandleBaseUndeletable(); TObject* GetObject() const {return fObject;} private: /// The actual pointer that will be reference counted. TObject* fObject; ClassDef(IHandleBaseUndeletable,2); }; /// An abstract base class for handles that's used to maintain the /// reference count. This is a deep internal class that can't be directly /// accessed. The original didn't define a constructor, or destructor so /// it won't interfere with templated objects that derive from this class, /// but they are needed by root so this class has them... class IVHandle: public TObject { protected: IVHandle(); virtual ~IVHandle(); /// Define default values for this object. void Default(IHandleBase* handle); /// Add a reference to the object being held by adding this IHandle to /// the reference list. void Link(const IVHandle& rhs); /// Remove a reference to the object being held by removing this /// IHandle from the reference list. Returns true if the last /// reference is removed. bool Unlink(); /// Delete the pointer if that is allowed by encapuslating all of the /// necessary logic. void Destroy(void); /// Safely get the pointer value for this handle. This hides the /// underlying storage model from the IHandle template. TObject* GetPointerValue() const; public: /// Release the ownership of the object being held by this handle. /// The responsiblity to delete the object passes to the calling /// routine. void Release(); /// Equality operator for all IHandle objects. bool operator == (const IVHandle& rhs) const; /// A deep debugging tool, DO NOT CALL!!!! IHandleBase* GetInternalHandle() const {return fHandle;} /// Print the hit information. virtual void ls(Option_t *opt = "") const; private: /// The reference counted handle. This handle contains the pointer to /// the actual data object. IHandleBase* fHandle; ClassDef(IVHandle,10); }; /// A handle class that will manage the lifetime (and ownership) of /// objects that are derived from TObject. This doesn't require any /// cooperation from the object that will be reference counted. This /// works fine even if the object is owned by ROOT, but really shines if /// the object is owned by the program. The resulting handle is a smart /// pointer and (in most respects) looks like a normal pointer. /// /// \warning IHandle objects which are used in an event loop \b must \b /// not be saved between events. Saving a IHandle between events will /// cause a memory leak when the output file is read by a later program. /// In addition, keep in mind that this is a reference counted object (see /// Wikipedia for details). This means that "reference loops" will /// prevent objects from being deleted. /// /// A reference to a NULL IHandle will throw an COMET::EHandleBadReference /// which means it won't generate a core dump. For debugging, you can run /// the problem under gdb, and set a break point for the /// COMET::EHandleBadReference constructor. /// /// \code /// catch throw 'COMET::EHandleBadReference::EHandleBadReference()' /// \endcode template class IHandle : public IVHandle { template friend class IHandle; template friend U* GetPointer(const IHandle& handle); public: /// Allow a null handle to be constructed. IHandle() { Default(NULL); } /// Explicitly construct a IHandle from a T pointer. If this isn't /// explicit, then C++ will use this constructor as a type conversion /// operator. Making it explicit gives the following behavior: /// \code /// Pointee* p = new Pointee; /// IHandle h(p); // O.K. /// IHandle g = h; // O.K. /// IHandle j = p; // won't compile /// \endcode /// While conversion from a pointer to a handle good on "paper", it's /// not. Internally, "h", and "g" share the ownership of "p" and /// manage so that "p" remains active until both "g" and "h" are /// destroyed. On the other hand, "h" and "j" both assume full /// ownership of "p" which gets deleted as soon as the first one gets /// deleted (leaving a dangling reference, a.k.a. BUG). explicit IHandle(T* pointee) { IHandleBase *base = NULL; if (pointee) base = new IHandleBaseDeletable(pointee); Default(base); } /// Create a IHandle for an object with an explicit ownership. This /// allows IHandle objects to refer to static objects, objects that /// are allocated in a buffer, or objects allocated on the stack. If /// the object is owned by the handle (and can be deleted), the owner /// argument is true. If the object is *not* owned by the handle, /// the owner argument is false. IHandle(T* pointee, bool owner) { if (pointee) { IHandleBase *base = NULL; if(owner) base= new IHandleBaseDeletable(pointee); else base= new IHandleBaseUndeletable(pointee); Default(base); } else { Default(NULL); } } /// The copy constructor for this handle. IHandle(const IHandle& rhs) : IVHandle(rhs) { Default(NULL); Link(rhs); } // Copy between classes. template IHandle(const IHandle& rhs) { Default(NULL); if (dynamic_cast(rhs.GetPointerValue())) { Link(rhs); } } /// The destructor for the IHandle object which may delete the pointer. virtual ~IHandle() { if (Unlink()) Destroy(); } /// @{ Assign one IHandle object to another. This should be designed /// to recast the pointee between the assignments so that an implicit /// conversion takes place. If the recast fails, the new pointer will /// be null. /// /// \code /// IHandle h(new IMCHit); /// IHandle mc; /// IHandle combo; /// mc = h; // casts pointer value of h to an IMCHit. /// if (mc) cout<<"true"; // prints "true" /// combo = h; // dynamic casts fails so combo holds NULL. /// if (combo) cout<<"true"; // No output since combo is NULL. /// \endcode /// /// This provides both const and non-const versions of the assignment. IHandle& operator = (IHandle& rhs) { if (operator == (rhs)) return rhs; // Going to replace the value of this smart pointer, so unref and // possible delete. if (Unlink()) Destroy(); // Make sure the handle in the default state. Default(NULL); // Compatible types if (dynamic_cast(rhs.GetPointerValue())) { Link(rhs); } return rhs; } const IHandle& operator = (const IHandle& rhs) { // Going to replace the value of this smart pointer, so unref and // possible delete. if (Unlink()) Destroy(); // Make sure the handle in the default state. Default(NULL); // Compatible types if (dynamic_cast(rhs.GetPointerValue())) { Link(rhs); } return rhs; } template IHandle& operator = (IHandle& rhs) { // Going to replace the value of this smart pointer, so unref and // possible delete. if (Unlink()) Destroy(); // Make sure the handle in the default state. Default(NULL); // Compatible types if (dynamic_cast(rhs.GetPointerValue())) { Link(rhs); } return rhs; } template const IHandle& operator = (const IHandle& rhs) { // Going to replace the value of this smart pointer, so unref and // possible delete. if (Unlink()) Destroy(); // Make sure the handle in the default state. Default(NULL); // Compatible types if (dynamic_cast(rhs.GetPointerValue())) { Link(rhs); } return rhs; } /// @} /// The reference operator T& operator*() const { TObject* object = GetPointerValue(); if (!object) { COMETError("Dereferencing a NULL handle " << typeid(T).name()); throw EHandleBadReference(); } T* pointer = dynamic_cast(object); if (!pointer) { COMETError("Dereferencing with an invalid cast " << typeid(T).name()); throw EHandleBadReference(); } return *pointer; } /// The redirection operator T* operator->() const { TObject* object = GetPointerValue(); if (!object) { COMETError("Referencing a NULL handle " << typeid(T).name()); throw EHandleBadReference(); } T* pointer = dynamic_cast(object); if (!pointer) { COMETError("Referencing with an invalid cast" << typeid(T).name()); throw EHandleBadReference(); } return pointer; } #ifndef __CINT__ private: /// Internal \internal /// Prevent the delete operator, while allowing /// \code /// IHandle h /// if (h) return true; /// \endcode class Tester { void operator delete(void*); public: void bogo() {} }; public: /// A conversion to a bogus type. This lets code like this /// /// \code /// IHandle h(new Pointee); /// if (h) return true; /// \endcode /// /// compile and work as expected. The trade off for this behavior is /// that we can't implement converters to the pointer type: /// /// \code /// IHandle h(new Pointee); /// Pointee *p = h; // Won't work. /// Pointee *p = GetPointer(h); // Will work. /// \endcode /// /// since it would introduce ambiguities and the compiler won't pick a /// method. Actually, that's probably a benefit, The conversion to a /// pointer should be done with GetPointer(); operator Tester*() const { if (!GetPointerValue()) return NULL; static Tester test; return &test; } #endif ClassDefT(IHandle,10); }; ClassDefT2(IHandle,T) /// Turn a IHandle object into a pointer. This is implemented as a /// function so that it is *really* clear that something funky is going /// on. You should always pass a IHandle, or a IHandle reference. template T* GetPointer(const IHandle& handle) { TObject* object = handle.GetPointerValue(); if (!object) return NULL; T* pointer = dynamic_cast(object); return pointer; } /// Make a comparision between two handles based on the pointer value. template bool operator <(const IHandle& a, const IHandle& b) { T* aPtr = GetPointer(a); U* bPtr = GetPointer(b); return (aPtr < bPtr); } } #endif