// Copyright (C) 2004, 2007 International Business Machines and others. // All Rights Reserved. // This code is published under the Common Public License. // // $Id: IpSmartPtr.hpp 1019 2007-06-24 03:52:34Z andreasw $ // // Authors: Carl Laird, Andreas Waechter IBM 2004-08-13 #ifndef __IPSMARTPTR_HPP__ #define __IPSMARTPTR_HPP__ #include "IpReferenced.hpp" #include "IpDebug.hpp" #if COIN_IPOPT_CHECKLEVEL > 2 # define IP_DEBUG_SMARTPTR #endif namespace Ipopt { /** Template class for Smart Pointers. * A SmartPtr behaves much like a raw pointer, but manages the lifetime * of an object, deleting the object automatically. This class implements * a reference-counting, intrusive smart pointer design, where all * objects pointed to must inherit off of ReferencedObject, which * stores the reference count. Although this is intrusive (native types * and externally authored classes require wrappers to be referenced * by smart pointers), it is a safer design. A more detailed discussion of * these issues follows after the usage information. * * Usage Example: * Note: to use the SmartPtr, all objects to which you point MUST * inherit off of ReferencedObject. * * \verbatim * * In MyClass.hpp... * * #include "IpReferenced.hpp" * namespace Ipopt { * * class MyClass : public ReferencedObject // must derive from ReferencedObject * { * ... * } * } // namespace Ipopt * * * In my_usage.cpp... * * #include "IpSmartPtr.hpp" * #include "MyClass.hpp" * * void func(AnyObject& obj) * { * SmartPtr ptr_to_myclass = new MyClass(...); * // ptr_to_myclass now points to a new MyClass, * // and the reference count is 1 * * ... * * obj.SetMyClass(ptr_to_myclass); * // Here, let's assume that AnyObject uses a * // SmartPtr internally here. * // Now, both ptr_to_myclass and the internal * // SmartPtr in obj point to the same MyClass object * // and its reference count is 2. * * ... * * // No need to delete ptr_to_myclass, this * // will be done automatically when the * // reference count drops to zero. * * } * * \endverbatim * * It is not necessary to use SmartPtr's in all cases where an * object is used that has been allocated "into" a SmartPtr. It is * possible to just pass objects by reference or regular pointers, * even if lower down in the stack a SmartPtr is to be held on to. * Everything should work fine as long as a pointer created by "new" * is immediately passed into a SmartPtr, and if SmartPtr's are used * to hold on to objects. * * Other Notes: * The SmartPtr implements both dereference operators -> & *. * The SmartPtr does NOT implement a conversion operator to * the raw pointer. Use the GetRawPtr() method when this * is necessary. Make sure that the raw pointer is NOT * deleted. * The SmartPtr implements the comparison operators == & != * for a variety of types. Use these instead of * \verbatim * if (GetRawPtr(smrt_ptr) == ptr) // Don't use this * \endverbatim * SmartPtr's, as currently implemented, do NOT handle circular references. * For example: consider a higher level object using SmartPtrs to point to * A and B, but A and B also point to each other (i.e. A has a SmartPtr * to B and B has a SmartPtr to A). In this scenario, when the higher * level object is finished with A and B, their reference counts will * never drop to zero (since they reference each other) and they * will not be deleted. This can be detected by memory leak tools like * valgrind. If the circular reference is necessary, the problem can be * overcome by a number of techniques: * * 1) A and B can have a method that "releases" each other, that is * they set their internal SmartPtrs to NULL. * \verbatim * void AClass::ReleaseCircularReferences() * { * smart_ptr_to_B = NULL; * } * \endverbatim * Then, the higher level class can call these methods before * it is done using A & B. * * 2) Raw pointers can be used in A and B to reference each other. * Here, an implicit assumption is made that the lifetime is * controlled by the higher level object and that A and B will * both exist in a controlled manner. Although this seems * dangerous, in many situations, this type of referencing * is very controlled and this is reasonably safe. * * 3) This SmartPtr class could be redesigned with the Weak/Strong * design concept. Here, the SmartPtr is identified as being * Strong (controls lifetime of the object) or Weak (merely * referencing the object). The Strong SmartPtr increments * (and decrements) the reference count in ReferencedObject * but the Weak SmartPtr does not. In the example above, * the higher level object would have Strong SmartPtrs to * A and B, but A and B would have Weak SmartPtrs to each * other. Then, when the higher level object was done with * A and B, they would be deleted. The Weak SmartPtrs in A * and B would not decrement the reference count and would, * of course, not delete the object. This idea is very similar * to item (2), where it is implied that the sequence of events * is controlled such that A and B will not call anything using * their pointers following the higher level delete (i.e. in * their destructors!). This is somehow safer, however, because * code can be written (however expensive) to perform run-time * detection of this situation. For example, the ReferencedObject * could store pointers to all Weak SmartPtrs that are referencing * it and, in its destructor, tell these pointers that it is * dying. They could then set themselves to NULL, or set an * internal flag to detect usage past this point. * * Comments on Non-Intrusive Design: * In a non-intrusive design, the reference count is stored somewhere other * than the object being referenced. This means, unless the reference * counting pointer is the first referencer, it must get a pointer to the * referenced object from another smart pointer (so it has access to the * reference count location). In this non-intrusive design, if we are * pointing to an object with a smart pointer (or a number of smart * pointers), and we then give another smart pointer the address through * a RAW pointer, we will have two independent, AND INCORRECT, reference * counts. To avoid this pitfall, we use an intrusive reference counting * technique where the reference count is stored in the object being * referenced. */ template class SmartPtr : public Referencer { public: #define dbg_smartptr_verbosity 0 /**@name Constructors/Destructors */ //@{ /** Default constructor, initialized to NULL */ SmartPtr(); /** Copy constructor, initialized from copy */ SmartPtr(const SmartPtr& copy); /** Constructor, initialized from T* ptr */ SmartPtr(T* ptr); /** Destructor, automatically decrements the * reference count, deletes the object if * necessary.*/ ~SmartPtr(); //@} /**@name Overloaded operators. */ //@{ /** Overloaded arrow operator, allows the user to call * methods using the contained pointer. */ T* operator->() const; /** Overloaded dereference operator, allows the user * to dereference the contained pointer. */ T& operator*() const; /** Overloaded equals operator, allows the user to * set the value of the SmartPtr from a raw pointer */ SmartPtr& operator=(T* rhs); /** Overloaded equals operator, allows the user to * set the value of the SmartPtr from another * SmartPtr */ SmartPtr& operator=(const SmartPtr& rhs); /** Overloaded equality comparison operator, allows the * user to compare the value of two SmartPtrs */ template friend bool operator==(const SmartPtr& lhs, const SmartPtr& rhs); /** Overloaded equality comparison operator, allows the * user to compare the value of a SmartPtr with a raw pointer. */ template friend bool operator==(const SmartPtr& lhs, U2* raw_rhs); /** Overloaded equality comparison operator, allows the * user to compare the value of a raw pointer with a SmartPtr. */ template friend bool operator==(U1* lhs, const SmartPtr& raw_rhs); /** Overloaded in-equality comparison operator, allows the * user to compare the value of two SmartPtrs */ template friend bool operator!=(const SmartPtr& lhs, const SmartPtr& rhs); /** Overloaded in-equality comparison operator, allows the * user to compare the value of a SmartPtr with a raw pointer. */ template friend bool operator!=(const SmartPtr& lhs, U2* raw_rhs); /** Overloaded in-equality comparison operator, allows the * user to compare the value of a SmartPtr with a raw pointer. */ template friend bool operator!=(U1* lhs, const SmartPtr& raw_rhs); //@} /**@name friend method declarations. */ //@{ /** Returns the raw pointer contained. * Use to get the value of * the raw ptr (i.e. to pass to other * methods/functions, etc.) * Note: This method does NOT copy, * therefore, modifications using this * value modify the underlying object * contained by the SmartPtr, * NEVER delete this returned value. */ template friend U* GetRawPtr(const SmartPtr& smart_ptr); /** Returns a const pointer */ template friend SmartPtr ConstPtr(const SmartPtr& smart_ptr); /** Returns true if the SmartPtr is NOT NULL. * Use this to check if the SmartPtr is not null * This is preferred to if(GetRawPtr(sp) != NULL) */ template friend bool IsValid(const SmartPtr& smart_ptr); /** Returns true if the SmartPtr is NULL. * Use this to check if the SmartPtr IsNull. * This is preferred to if(GetRawPtr(sp) == NULL) */ template friend bool IsNull(const SmartPtr& smart_ptr); //@} private: /**@name Private Data/Methods */ //@{ /** Actual raw pointer to the object. */ T* ptr_; /** Set the value of the internal raw pointer * from another raw pointer, releasing the * previously referenced object if necessary. */ SmartPtr& SetFromRawPtr_(T* rhs); /** Set the value of the internal raw pointer * from a SmartPtr, releasing the previously referenced * object if necessary. */ SmartPtr& SetFromSmartPtr_(const SmartPtr& rhs); /** Release the currently referenced object. */ void ReleasePointer_(); //@} }; /**@name SmartPtr friend function declarations.*/ //@{ template U* GetRawPtr(const SmartPtr& smart_ptr); template SmartPtr ConstPtr(const SmartPtr& smart_ptr); template bool IsNull(const SmartPtr& smart_ptr); template bool IsValid(const SmartPtr& smart_ptr); template bool operator==(const SmartPtr& lhs, const SmartPtr& rhs); template bool operator==(const SmartPtr& lhs, U2* raw_rhs); template bool operator==(U1* lhs, const SmartPtr& raw_rhs); template bool operator!=(const SmartPtr& lhs, const SmartPtr& rhs); template bool operator!=(const SmartPtr& lhs, U2* raw_rhs); template bool operator!=(U1* lhs, const SmartPtr& raw_rhs); //@} template SmartPtr::SmartPtr() : ptr_(NULL) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("SmartPtr::SmartPtr()", dbg_smartptr_verbosity); #endif #ifdef CHECK_SMARTPTR const ReferencedObject* trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = ptr_; trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = NULL; #endif } template SmartPtr::SmartPtr(const SmartPtr& copy) : ptr_(NULL) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("SmartPtr::SmartPtr(const SmartPtr& copy)", dbg_smartptr_verbosity); #endif #ifdef CHECK_SMARTPTR const ReferencedObject* trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = ptr_; trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = NULL; #endif (void) SetFromSmartPtr_(copy); } template SmartPtr::SmartPtr(T* ptr) : ptr_(NULL) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("SmartPtr::SmartPtr(T* ptr)", dbg_smartptr_verbosity); #endif #ifdef CHECK_SMARTPTR const ReferencedObject* trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = ptr_; trying_to_use_SmartPtr_with_an_object_that_does_not_inherit_from_ReferencedObject_ = NULL; #endif (void) SetFromRawPtr_(ptr); } template SmartPtr::~SmartPtr() { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("SmartPtr::~SmartPtr(T* ptr)", dbg_smartptr_verbosity); #endif ReleasePointer_(); } template T* SmartPtr::operator->() const { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("T* SmartPtr::operator->()", dbg_smartptr_verbosity); #endif // cannot deref a null pointer #if COIN_IPOPT_CHECKLEVEL > 0 assert(ptr_); #endif return ptr_; } template T& SmartPtr::operator*() const { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("T& SmartPtr::operator*()", dbg_smartptr_verbosity); #endif // cannot dereference a null pointer #if COIN_IPOPT_CHECKLEVEL > 0 assert(ptr_); #endif return *ptr_; } template SmartPtr& SmartPtr::operator=(T* rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH("SmartPtr& SmartPtr::operator=(T* rhs)", dbg_smartptr_verbosity); #endif return SetFromRawPtr_(rhs); } template SmartPtr& SmartPtr::operator=(const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH( "SmartPtr& SmartPtr::operator=(const SmartPtr& rhs)", dbg_smartptr_verbosity); #endif return SetFromSmartPtr_(rhs); } template SmartPtr& SmartPtr::SetFromRawPtr_(T* rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH( "SmartPtr& SmartPtr::SetFromRawPtr_(T* rhs)", dbg_smartptr_verbosity); #endif // Release any old pointer ReleasePointer_(); if (rhs != NULL) { rhs->AddRef(this); ptr_ = rhs; } return *this; } template SmartPtr& SmartPtr::SetFromSmartPtr_(const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH( "SmartPtr& SmartPtr::SetFromSmartPtr_(const SmartPtr& rhs)", dbg_smartptr_verbosity); #endif T* ptr = GetRawPtr(rhs); /* AW: I changed this so that NULL is correctly copied from the right hand side */ // if (ptr != NULL) { // SetFromRawPtr_(ptr); // } SetFromRawPtr_(ptr); return (*this); } template void SmartPtr::ReleasePointer_() { #ifdef IP_DEBUG_SMARTPTR DBG_START_METH( "void SmartPtr::ReleasePointer()", dbg_smartptr_verbosity); #endif if (ptr_) { ptr_->ReleaseRef(this); if (ptr_->ReferenceCount() == 0) { delete ptr_; } ptr_ = NULL; } } template U* GetRawPtr(const SmartPtr& smart_ptr) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "T* GetRawPtr(const SmartPtr& smart_ptr)", 0); #endif return smart_ptr.ptr_; } template SmartPtr ConstPtr(const SmartPtr& smart_ptr) { // compiler should implicitly cast return GetRawPtr(smart_ptr); } template bool IsValid(const SmartPtr& smart_ptr) { return !IsNull(smart_ptr); } template bool IsNull(const SmartPtr& smart_ptr) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool IsNull(const SmartPtr& smart_ptr)", 0); #endif return (smart_ptr.ptr_ == NULL); } template bool ComparePointers(const U1* lhs, const U2* rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool ComparePtrs(const U1* lhs, const U2* rhs)", dbg_smartptr_verbosity); #endif if (lhs == rhs) { return true; } // Even if lhs and rhs point to the same object // with different interfaces U1 and U2, we cannot guarantee that // the value of the pointers will be equivalent. We can // guarantee this if we convert to void* const void* v_lhs = static_cast(lhs); const void* v_rhs = static_cast(rhs); if (v_lhs == v_rhs) { return true; } // They must not be the same return false; } template bool operator==(const SmartPtr& lhs, const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator==(const SmartPtr& lhs, const SmartPtr& rhs)", dbg_smartptr_verbosity); #endif U1* raw_lhs = GetRawPtr(lhs); U2* raw_rhs = GetRawPtr(rhs); return ComparePointers(raw_lhs, raw_rhs); } template bool operator==(const SmartPtr& lhs, U2* raw_rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator==(SmartPtr& lhs, U2* rhs)", dbg_smartptr_verbosity); #endif U1* raw_lhs = GetRawPtr(lhs); return ComparePointers(raw_lhs, raw_rhs); } template bool operator==(U1* raw_lhs, const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator==(U1* raw_lhs, SmartPtr& rhs)", dbg_smartptr_verbosity); #endif const U2* raw_rhs = GetRawPtr(rhs); return ComparePointers(raw_lhs, raw_rhs); } template bool operator!=(const SmartPtr& lhs, const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator!=(const SmartPtr& lhs, const SmartPtr& rhs)", dbg_smartptr_verbosity); #endif bool retValue = operator==(lhs, rhs); return !retValue; } template bool operator!=(const SmartPtr& lhs, U2* raw_rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator!=(SmartPtr& lhs, U2* rhs)", dbg_smartptr_verbosity); #endif bool retValue = operator==(lhs, raw_rhs); return !retValue; } template bool operator!=(U1* raw_lhs, const SmartPtr& rhs) { #ifdef IP_DEBUG_SMARTPTR DBG_START_FUN( "bool operator!=(U1* raw_lhs, SmartPtr& rhs)", dbg_smartptr_verbosity); #endif bool retValue = operator==(raw_lhs, rhs); return !retValue; } } // namespace Ipopt #endif