/** @file smart_ptr.h @brief smart_ptr is an alias for whatever smart pointer type we are using. Copyright (C) 2004 SolidWorks Corporation Currently we're using boost::intrusive_ptr. @author Nat Goodspeed @date 01/13/04 **/ #if ! defined(UTIL_SMART_PTR_H) #define UTIL_SMART_PTR_H #include "Platforms/PlatformDefs.h" #if defined(SW_MSVC71) #pragma warning(disable:4275) // complaints about non-DLL interface used as base class #endif #include // std::swap() template class smart_ptr; /** Use of boost::intrusive_ptr means that you have to be able to find a refcount, given a pointer to the managed object. One fairly painless way to do that is to derive every managed class from smart_base. */ class smart_base { private: // Want to be able to maintain refcount even for const smart_bases mutable int _refCount; public: smart_base() : _refCount(0) { } smart_base(const smart_base&) : _refCount(0) { } smart_base& operator=(const smart_base&) { return *this; } // see Meyers, p196 virtual ~smart_base() { } protected: template friend class smart_ptr; /** @name refcount management The fact that addRef() and releaseRef() are virtual means that a particular managed class can intercept them to do special things. */ //@{ virtual void addRef() const { ++_refCount; } virtual void releaseRef() const { if (--_refCount <= 0) const_cast(this)->freeRef(); } //@} /** Back in the bad old days of MSVC++ 5.0, if a base-class object issued delete this, where @c this was of course of base-class type, the runtime wouldn't execute any subclass destructor -- even if all destructors in question were explicitly marked @c virtual! That meant that every smart_base leaf subclass needed its own override for the freeRef() method, even if all it contained was delete this, so that @c this would be of correct type. But that bug has long since been fixed, and most smart_base subclasses can safely ignore the freeRef() method. */ virtual void freeRef() { delete this; } int getRefCount() const { return _refCount; } }; /** Helper macros to make it easy to define smart pointers to opaque (forward-declared) base classes */ /** This one goes in the header of non-DLLs */ #define SMART_OPAQUE_PTR_DECL(classname) /** This one goes in the header of DLLs */ #define SMART_OPAQUE_PTR_DECL_DLL(api, classname) /** This one goes in the C++ file */ #define SMART_OPAQUE_PTR_DEF(classname) /** This macro is for two common smart pointer typedefs. It also forward-declares the class. */ #define SMART_PTR_TYPEDEFS(classname) \ class classname; \ typedef smart_ptr classname##Ptr; \ typedef smart_ptr classname##ConstPtr; /** This class defines our implementation of smart pointers. It is essentially a boost intrusive_ptr with the notable exception that it only works with classes derived from smart_base. Besides the usual pointer of type T to the object, smart_ptr stores an additional pointer to the same object, but of type smart_base. This is so that one can manipulate a smart_ptr to a forward declared class, i.e. when the compiler does not know that T is actually derived from smart_base. Originally I thought a single pointer of type smart_base would be sufficient (as it can be statically cast to T* at dereference time), but when smart_base is a virtual base class of T, static_cast fails to compile. */ template class smart_ptr { public: typedef T element_type; //------------constructors------------------------------- smart_ptr() : ptr(NULL), typedPtr(NULL) {} /** This constructor implicitly checks that T is derived from smart_base. Therefore, it cannot be used when T is only forward declared. This is typically not a problem, except when we want to create a smart_ptr from NULL, which now must be done using the default constructor. */ smart_ptr(T *inPtr) : ptr(inPtr), typedPtr(inPtr) { if(ptr) ptr->addRef(); } /** generic copy constructor */ template smart_ptr(const smart_ptr &smartPtr) : ptr(smartPtr.ptr), typedPtr(smartPtr.typedPtr) { if(ptr) ptr->addRef(); } /** Official copy constructor. Apparently the C++ standard mandates that a templated constructor doesn't count as a copy constructor, so we must also define a "real" copy constructor. Otherwise, C++ automatically creates a copy constructor that does not do reference counting. */ smart_ptr(const smart_ptr &smartPtr) : ptr(smartPtr.ptr), typedPtr(smartPtr.typedPtr) { if(ptr) ptr->addRef(); } //------------destructor------------------------------- ~smart_ptr() { if(ptr) ptr->releaseRef(); } //members T *get() const { return typedPtr; } void reset() { if(ptr) ptr->releaseRef(); ptr = NULL; typedPtr = NULL; } void swap(smart_ptr &other) { std::swap(ptr, other.ptr); std::swap(typedPtr, other.typedPtr); } //------------operators------------------------------- T *operator->() const { return typedPtr; } T &operator*() const { return *typedPtr; } /** Swapping with a temporary is shorter to write and means that all reference counting is done in constructors/destructors: fewer places for bugs */ template smart_ptr &operator=(const smart_ptr &other) { // Each of these operator=() methods uses (temp).swap(*this) as its // idiom to express the assignment. // 1. Constructing the temp increments the ref count for the rhs of // the assignment. // 2. The swap not only copies the new value into *this, it also // preserves the old value by stuffing it into the temp. // 3. Swapping it into the temp ensures that when the temp is // destroyed, the ref count for the old value is properly // decremented, with appropriate side effects as needed. // 4. Using the same temp for twiddling both ref counts avoids the // dreaded assign-to-self bug. A naïve implementation might // decrement the old value’s ref count first before incrementing // the new value’s. If this is the last smart pointer referencing // that object, assigning to self could leave you with a deadly // dangling reference to freed memory. // All this and it's exception-safe too! In fact it's an instance of a // more general idiom embraced by Herb Sutter in Exceptional C++ (see // Item 13 in particular): any work that might throw is performed on a // temporary object. If the method is aborted by an exception, the // temporary is cleaned up, leaving *this still in a consistent // state. Only when we're sure it did NOT throw do we actually change // *this using our no-throw swap() method. smart_ptr(other).swap(*this); return *this; } /** Just like the copy constructor the templated assignment operator doesn't replace the compiler-generated default assignment operator. Hence this method. */ smart_ptr &operator=(const smart_ptr &other) { smart_ptr(other).swap(*this); return *this; } /** assignment from dumb pointer */ smart_ptr &operator=(T *other) { smart_ptr(other).swap(*this); return *this; } /** For sets of smart pointers, we want comparisons */ template bool operator<(const smart_ptr &other) const { return ptr < other.ptr; } template bool operator==(const smart_ptr &other) const { return ptr == other.ptr; } template bool operator!=(const smart_ptr &other) const { return ptr != other.ptr; } typedef const smart_base *smart_ptr::*unspecified_bool_type; /** this is basically taken from intrusive_ptr.hpp in boost */ operator unspecified_bool_type () const { return ptr == 0 ? 0 : &smart_ptr::ptr; } //this seems silly but it's necessary for smart_ptr to access smart_ptr template friend class smart_ptr; private: const smart_base *ptr; T *typedPtr; }; namespace std { template void swap(smart_ptr &p1, smart_ptr &p2) { p1.swap(p2); } } namespace boost { template smart_ptr dynamic_pointer_cast(const smart_ptr &p) { return smart_ptr(dynamic_cast(p.get())); } } template T* get_pointer(smart_ptr const & p) { return p.get(); } #endif /* ! defined(UTIL_SMART_PTR_H) */